9090import de .symeda .sormas .api .person .Sex ;
9191import de .symeda .sormas .api .sample .AdditionalTestDto ;
9292import de .symeda .sormas .api .sample .AdditionalTestType ;
93+ import de .symeda .sormas .api .sample .PathogenTestDto ;
94+ import de .symeda .sormas .api .sample .PathogenTestResultType ;
9395import de .symeda .sormas .api .sample .PathogenTestType ;
96+ import de .symeda .sormas .api .sample .ResultValueType ;
9497import de .symeda .sormas .api .sample .SampleDto ;
9598import de .symeda .sormas .api .sample .SampleMaterial ;
9699import de .symeda .sormas .api .sample .SamplePurpose ;
97100import de .symeda .sormas .api .sample .SimpleTestResultType ;
98101import de .symeda .sormas .api .sample .SpecimenCondition ;
102+ import de .symeda .sormas .api .user .UserDto ;
99103import de .symeda .sormas .api .user .UserReferenceDto ;
100104import de .symeda .sormas .api .utils .DateHelper ;
105+ import de .symeda .sormas .api .utils .Diseases ;
101106import de .symeda .sormas .api .utils .SortProperty ;
102107import de .symeda .sormas .api .utils .UtilDate ;
103108import de .symeda .sormas .api .utils .ValidationRuntimeException ;
@@ -126,6 +131,9 @@ public class DevModeView extends AbstractConfigurationView {
126131
127132 private static Random randomGenerator ;
128133
134+ private static final List <PathogenTestResultType > PATHOGEN_TEST_RESULT_POOL =
135+ Arrays .asList (PathogenTestResultType .POSITIVE , PathogenTestResultType .NEGATIVE , PathogenTestResultType .PENDING );
136+
129137 private TextField seedField ;
130138 private CheckBox useManualSeedCheckbox ;
131139 private static boolean useManualSeed = false ;
@@ -195,9 +203,7 @@ private HorizontalLayout createDevButtonsLayout() {
195203 Button btnClearCaseVaccinationStatuses = ButtonHelper .createButton (
196204 "clearCaseVaccinationStatuses" ,
197205 "Clear case vaccination statuses" ,
198- e -> showClearVaccinationStatusesPopup (
199- "Clear case vaccination statuses" ,
200- FacadeProvider .getCaseFacade ()::deleteVaccinationStatuses ));
206+ e -> showClearVaccinationStatusesPopup ("Clear case vaccination statuses" , FacadeProvider .getCaseFacade ()::deleteVaccinationStatuses ));
201207 horizontalLayout .addComponent (btnClearCaseVaccinationStatuses );
202208
203209 Button btnClearContactVaccinationStatuses = ButtonHelper .createButton (
@@ -1160,6 +1166,19 @@ private void generateSamples(SampleGenerationConfig sampleGenerationConfig) {
11601166
11611167 UserReferenceDto user = UiUtil .getUserReference ();
11621168
1169+ // Resolve the disease once, up front: pick a random one if none was selected and freeze it for the whole
1170+ // run. This must happen BEFORE the case query so the cases are actually scoped to this disease — otherwise
1171+ // the generated pathogen tests would carry a testedDisease that contradicts their case.
1172+ Disease disease = sampleGenerationConfig .getDisease ();
1173+ if (disease == null ) {
1174+ disease = random (FacadeProvider .getDiseaseConfigurationFacade ().getAllDiseases (true , true , true ));
1175+ sampleGenerationConfig .setDisease (disease );
1176+ }
1177+
1178+ // The pickable test types only depend on the (now fixed) disease, so compute them once instead of per sample.
1179+ List <PathogenTestType > pickablePathogenTestTypes =
1180+ sampleGenerationConfig .isRequestPathogenTestsToBePerformed () ? getPickablePathogenTestTypes (disease ) : Collections .emptyList ();
1181+
11631182 List <CaseReferenceDto > cases = FacadeProvider .getCaseFacade ()
11641183 .getRandomCaseReferences (
11651184 new CaseCriteria ().region (sampleGenerationConfig .getRegion ())
@@ -1168,18 +1187,11 @@ private void generateSamples(SampleGenerationConfig sampleGenerationConfig) {
11681187 sampleGenerationConfig .getEntityCountAsNumber () * 2 ,
11691188 random ());
11701189
1171- if (nonNull (cases )) {
1190+ if (nonNull (cases ) && ! cases . isEmpty () ) {
11721191 for (int i = 0 ; i < sampleGenerationConfig .getEntityCountAsNumber (); i ++) {
11731192
11741193 CaseReferenceDto caseReference = random (cases );
11751194
1176- List <Disease > diseases = FacadeProvider .getDiseaseConfigurationFacade ().getAllDiseases (true , true , true );
1177- Disease disease = sampleGenerationConfig .getDisease ();
1178- if (disease == null ) {
1179- disease = random (diseases );
1180- sampleGenerationConfig .setDisease (disease );
1181- }
1182-
11831195 LocalDateTime referenceDateTime = getReferenceDateTime (
11841196 i ,
11851197 sampleGenerationConfig .getEntityCountAsNumber (),
@@ -1203,16 +1215,6 @@ private void generateSamples(SampleGenerationConfig sampleGenerationConfig) {
12031215
12041216 sample .setLab (sampleGenerationConfig .getLaboratory ());
12051217
1206- if (sampleGenerationConfig .isRequestPathogenTestsToBePerformed ()) {
1207- Set pathogenTestTypes = new HashSet <PathogenTestType >();
1208- int until = randomInt (1 , PathogenTestType .values ().length );
1209- for (int j = 0 ; j < until ; j ++) {
1210- pathogenTestTypes .add (PathogenTestType .values ()[j ]);
1211- }
1212- sample .setPathogenTestingRequested (true );
1213- sample .setRequestedPathogenTests (pathogenTestTypes );
1214- }
1215-
12161218 if (sampleGenerationConfig .isRequestAdditionalTestsToBePerformed ()) {
12171219 Set additionalTestTypes = new HashSet <AdditionalTestType >();
12181220 int until = randomInt (1 , AdditionalTestType .values ().length );
@@ -1238,6 +1240,10 @@ private void generateSamples(SampleGenerationConfig sampleGenerationConfig) {
12381240
12391241 SampleDto sampleDto = FacadeProvider .getSampleFacade ().saveSample (sample );
12401242
1243+ if (sampleGenerationConfig .isRequestPathogenTestsToBePerformed ()) {
1244+ createPathogenTests (sampleDto , disease , date , pickablePathogenTestTypes );
1245+ }
1246+
12411247 if (sampleGenerationConfig .isRequestAdditionalTestsToBePerformed ()) {
12421248 createAdditionalTest (sampleDto , date );
12431249 }
@@ -1254,7 +1260,7 @@ private void generateSamples(SampleGenerationConfig sampleGenerationConfig) {
12541260 logger .info (msg );
12551261 Notification .show ("" , msg , Notification .Type .TRAY_NOTIFICATION );
12561262 } else {
1257- String msg = "No Sample has been generated because cases is null " ;
1263+ String msg = "No samples generated: no cases found for the selected region/district/disease. " ;
12581264 logger .info (msg );
12591265 Notification .show ("" , msg , Notification .Type .TRAY_NOTIFICATION );
12601266 }
@@ -1294,6 +1300,73 @@ private void createAdditionalTest(SampleDto sample, Date date) {
12941300 FacadeProvider .getAdditionalTestFacade ().saveAdditionalTest (additionalTestDto );
12951301 }
12961302
1303+ /**
1304+ * The pathogen test types that are valid for the given disease and selectable for new tests. Antibiotic
1305+ * susceptibility is excluded because it requires a separate drug-susceptibility result. Mirrors the rules the
1306+ * real pathogen test form uses to populate its test-type combo.
1307+ */
1308+ private List <PathogenTestType > getPickablePathogenTestTypes (Disease disease ) {
1309+ return Diseases .DiseasesConfiguration .getVisibleValues (PathogenTestType .class , disease )
1310+ .stream ()
1311+ .filter (PathogenTestType ::isSelectableForNewTests )
1312+ .filter (type -> type != PathogenTestType .ANTIBIOTIC_SUSCEPTIBILITY )
1313+ .collect (Collectors .toList ());
1314+ }
1315+
1316+ private void createPathogenTests (SampleDto sample , Disease disease , Date date , List <PathogenTestType > pickableTypes ) {
1317+
1318+ if (pickableTypes .isEmpty ()) {
1319+ return ;
1320+ }
1321+
1322+ final UserDto currentUser = UiUtil .getUser ();
1323+ if (currentUser == null ) {
1324+ return ;
1325+ }
1326+
1327+ int count = randomInt (1 , 4 );
1328+ for (int i = 0 ; i < count ; i ++) {
1329+ PathogenTestDto test = PathogenTestDto .build (sample , currentUser );
1330+ test .setTestedDisease (disease );
1331+ // build() already resolves the lab from the current user, falling back to the sample's lab. Don't
1332+ // override it here (that would discard the user lab and re-introduce a null lab when the sample has none).
1333+ test .setTestType (random (pickableTypes ));
1334+ test .setTestResult (random (PATHOGEN_TEST_RESULT_POOL ));
1335+ test .setTestDateTime (date );
1336+
1337+ // Populate a quantitative value for half the tests so the inline quantitative display has data.
1338+ if (randomPercent (50 )) {
1339+ Set <ResultValueType > valueTypes = PathogenTestType .getResultValueTypes (test .getTestType ());
1340+ if (valueTypes .contains (ResultValueType .NUMERIC )) {
1341+ test .setQuantitativeValue (random ().nextFloat () * 1000 );
1342+ test .setQuantitativeUnit ("copies/mL" );
1343+ } else if (valueTypes .contains (ResultValueType .TEXT )) {
1344+ test .setQuantitativeText ("Generated result" );
1345+ }
1346+ }
1347+
1348+ // Populate free-text result details for some tests so the result-details indicator has data to surface.
1349+ if (randomPercent (40 )) {
1350+ test .setTestResultText ("Generated result details for testing the lab results view." );
1351+ }
1352+ if (randomPercent (30 )) {
1353+ test .setPerformedByReferenceLaboratory (true );
1354+ }
1355+ if (randomPercent (20 )) {
1356+ test .setRetestRequested (true );
1357+ }
1358+
1359+ try {
1360+ // Note: each save triggers the full pathogen-test cascade (sample/case result derivation,
1361+ // case reclassification, lab-result notifications, and Sormas-to-Sormas share sync when configured).
1362+ // That is acceptable for a dev-data tool.
1363+ FacadeProvider .getPathogenTestFacade ().savePathogenTest (test );
1364+ } catch (RuntimeException e ) {
1365+ logger .warn ("Failed to generate a pathogen test for sample {}: {}" , sample .getUuid (), e .getMessage ());
1366+ }
1367+ }
1368+ }
1369+
12971370 private void generateContacts () {
12981371 ContactGenerationConfig contactGenerationConfig = contactGeneratorConfigBinder .getBean ();
12991372 boolean valid = true ;
0 commit comments