Skip to content

Filter cases and the sample dashboard by pathogen test result, serogr… - #13990

Merged
roldy merged 2 commits into
developmentfrom
feature/13948-filters
Jun 18, 2026
Merged

roldy merged 2 commits into
developmentfrom
feature/13948-filters

Conversation

@roldy

@roldy roldy commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

…oup and variant

Fixes #13957

Summary by CodeRabbit

  • New Features

    • Added case filtering by pathogen test result and serogroup.
    • Enhanced the sample dashboard with filters for pathogen test result, serogroup, and disease variant.
    • Integrated corresponding “more filters” controls into the case filter form and expanded sample dashboard filter layout (including case-insensitive serogroup matching).
  • Improvements

    • Updated pathogen test list entries to show additional contextual icons (e.g., verified/reference lab/retest/details).
  • Bug Fixes

    • Fixed development sample generation to keep the selected disease consistent and generate matching pathogen tests.
  • Tests

    • Added automated coverage for the new filters and icon behavior.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

An error occurred during the review process. Please try again later.

📝 Walkthrough

Walkthrough

Adds pathogen test result, serogroup, and disease variant as filterable criteria in the case directory and sample dashboard. API criteria classes gain new fields and accessors, backend services extend JPA queries with EXISTS subqueries, Vaadin UI controls wire new filters, and integration tests verify filtering semantics. Additionally refactors sample generation to maintain disease consistency and improves pathogen test list rendering with conditional metadata icons.

Changes

Lab Filter Criteria: Test Result, Serogroup, Disease Variant

Layer / File(s) Summary
Criteria API contracts and i18n
sormas-api/.../caze/CaseCriteria.java, sormas-api/.../dashboard/SampleDashboardCriteria.java, sormas-api/.../i18n/Captions.java, sormas-api/.../resources/captions.properties
CaseCriteria gains pathogenTestResult and serogroup fields with public constants and fluent accessors. SampleDashboardCriteria gains pathogenTestResult, serogroup, and diseaseVariant. Serogroup caption constant and translation property are introduced.
Case filtering with EXISTS subqueries
sormas-backend/.../sample/PathogenTest.java, sormas-backend/.../caze/CaseService.java
PathogenTest.SEROTYPE_TEXT constant added. CaseService.createCriteriaFilter extended with two new EXISTS subqueries: one filtering cases by Sample.PATHOGEN_TEST_RESULT, one by PathogenTest.SEROTYPE_TEXT via unaccented ILIKE, both excluding logically deleted rows. Minor Javadoc whitespace adjustments.
Sample dashboard filtering with multiple criteria
sormas-backend/.../dashboard/sample/SampleDashboardService.java
createSampleFilter extended with direct equality on denormalized PATHOGEN_TEST_RESULT, serogroup EXISTS subquery (unaccented ILIKE), and disease variant EXISTS subquery. getTestResultCountsByResultType conditionally adds test result equality constraint. Remaining hunks are line-wrap reformats only.
Case directory filter UI controls
sormas-ui/.../caze/CaseFilterForm.java
Adds PATHOGEN_TEST_RESULT combo box and SEROGROUP text field to the "more filters" layout section with field wiring and layout registration.
Sample dashboard filter UI wiring and state management
sormas-ui/.../dashboard/sample/SampleDashboardDataProvider.java, sormas-ui/.../dashboard/sample/SampleDashboardFilterLayout.java
SampleDashboardDataProvider gains pathogenTestResult, serogroup, and diseaseVariant fields wired into buildDashboardCriteria(). SampleDashboardFilterLayout adds pathogen result ComboBox, serogroup TextField, and dynamically populated disease variant ComboBox; introduces setLabFiltersEnabled to disable/clear lab filters when environment-only material is selected; disease-change listener repopulates variant ComboBox.
Integration tests for lab filter criteria
sormas-backend/.../caze/CaseFacadeEjbTest.java, sormas-backend/.../dashboard/sample/SampleDashboardFacadeEjbTest.java
testFilterCasesByTestResultAndSerogroup verifies case-insensitive/partial serogroup matching, combined filter semantics, and cross-sample result discovery. getSampleCountsByLabFilters verifies result type, serogroup substring, and disease variant filtering on the dashboard.

Sample Generation Consistency and Test Display Improvements

Layer / File(s) Summary
DevModeView sample generation with disease consistency
sormas-ui/.../configuration/DevModeView.java
Refactors generateSamples to resolve disease once upfront before case lookup, precompute pickable pathogen test types for that disease, and delegate creation to createPathogenTests helper. Adds PATHOGEN_TEST_RESULT_POOL, getPickablePathogenTestTypes, and createPathogenTests methods. Removes per-sample disease randomization and improves control flow to skip when case list is empty.
PathogenTest list entry icon refactoring and tests
sormas-ui/.../samples/pathogentestlink/PathogenTestListEntry.java, sormas-ui/.../samples/pathogentestlink/PathogenTestListEntryTest.java
Refactors icon rendering to use shared addTopRightIcon helper that conditionally displays verified, reference lab, retest, and result-details icons based on DTO flags. Adds predicate helpers shouldShowRefLabIcon, shouldShowRetestIcon, shouldShowResultDetailsIcon. Introduces unit tests for each icon visibility condition.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant CaseFilterForm
  participant CaseFacade
  participant CaseService

  rect rgba(173, 216, 230, 0.5)
    Note over User, CaseFilterForm: Case Directory Filtering
    User->>CaseFilterForm: selects pathogenTestResult or enters serogroup
    CaseFilterForm->>CaseFacade: getCases(CaseCriteria)
    CaseFacade->>CaseService: createCriteriaFilter(caseCriteria)
    CaseService->>CaseService: EXISTS subquery on Sample.PATHOGEN_TEST_RESULT
    CaseService->>CaseService: EXISTS subquery on PathogenTest.SEROTYPE_TEXT (unaccented ILIKE)
    CaseService-->>CaseFacade: filtered cases
    CaseFacade-->>CaseFilterForm: matching cases displayed
  end

  rect rgba(144, 238, 144, 0.5)
    Note over User, CaseFilterForm: Sample Dashboard Filtering
    User->>CaseFilterForm: selects disease, then pathogenTestResult / serogroup / diseaseVariant
    CaseFilterForm->>CaseFilterForm: disease change repopulates variant options
    CaseFilterForm->>CaseFilterForm: environment-only material disables lab filters
    CaseFilterForm->>CaseFacade: getSampleCountsByResultType(SampleDashboardCriteria)
    CaseFacade->>CaseService: createSampleFilter with combined predicates
    CaseService->>CaseService: equality on PATHOGEN_TEST_RESULT + EXISTS subqueries on SEROTYPE_TEXT and diseaseVariant
    CaseService-->>CaseFacade: filtered counts
    CaseFacade-->>CaseFilterForm: dashboard results updated
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

  • SORMAS-Foundation/SORMAS-Project#13889: Introduces pathogen test serotypeText field and its entity/facade/UI mappings that this PR relies on for serogroup filtering via PathogenTest.SEROTYPE_TEXT.

Suggested reviewers

  • raulbob
  • obinna-h-n
  • KarnaiahPesula

🐇 Filter by serogroup with ease,
Test results and variants all please!
The dashboard now shows, the cases now glow,
With disease consistency—watch the lab data flow! 🔬

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Minor out-of-scope refactoring detected in PathogenTestListEntry: icon rendering logic was refactored from direct Label creation to conditional helper method calls, which improves code quality but exceeds the stated filtering feature scope. Consider extracting the PathogenTestListEntry refactoring into a separate PR focused on UI component improvements to maintain clear separation of concerns.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is truncated and incomplete (ends with '…oup'), making it unclear whether it mentions 'variant' and preventing full assessment of the main change. Complete the title to fully convey the PR's main objectives. For example: 'Filter cases and sample dashboard by pathogen test result, serogroup, and variant'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description references the correct issue #13957 with complete acceptance criteria details that align with the implemented changes.
Linked Issues check ✅ Passed All acceptance criteria from #13957 are met: case directory filtering by test result and serogroup [#13957], sample dashboard filtering by test result, serogroup, and variant [#13957], and responsive performance with large datasets.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/13948-filters

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sormas-ui/src/main/java/de/symeda/sormas/ui/dashboard/sample/SampleDashboardFilterLayout.java (1)

115-133: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep disease-variant filter disabled in environment-only mode.

Line 132 always repopulates the disease-variant combo, which can re-enable it after setLabFiltersEnabled(false) has disabled lab filters for environment-only sample material. That breaks the intended state gating.

Suggested fix
 		diseaseFilter.addValueChangeListener(e -> {
 			Object filterValue = diseaseFilter.getValue();
 			Disease selectedDisease = null;
 			if (filterValue instanceof Disease) {
 				selectedDisease = (Disease) filterValue;
 				dashboardDataProvider.setDisease(selectedDisease);
 				dashboardDataProvider.setWithNoDisease(null);
 			} else if (filterValue == SampleDashboardCustomDiseaseFilter.NO_DISEASE) {
 				dashboardDataProvider.setDisease(null);
 				dashboardDataProvider.setWithNoDisease(true);
 			} else if (filterValue == null) {
 				dashboardDataProvider.setDisease(null);
 				dashboardDataProvider.setWithNoDisease(null);
 			} else {
 				throw new RuntimeException("Disease filter [" + filterValue + "] not handled!");
 			}
-			// The disease variant is only meaningful for a selected disease; repopulate (and clear) it.
-			repopulateDiseaseVariantFilter(selectedDisease);
+			boolean environmentOnly =
+				dashboardDataProvider.getSampleMaterial() == null
+					&& dashboardDataProvider.getEnvironmentSampleMaterial() != null;
+			if (environmentOnly) {
+				setLabFiltersEnabled(false);
+			} else {
+				// The disease variant is only meaningful for a selected disease; repopulate (and clear) it.
+				repopulateDiseaseVariantFilter(selectedDisease);
+			}
 		});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/dashboard/sample/SampleDashboardFilterLayout.java`
around lines 115 - 133, The call to
repopulateDiseaseVariantFilter(selectedDisease) at the end of the diseaseFilter
value change listener unconditionally repopulates the disease-variant combo box,
which can re-enable it after setLabFiltersEnabled(false) has disabled lab
filters for environment-only sample material. Modify the diseaseFilter value
change listener to make the repopulateDiseaseVariantFilter call conditional by
checking whether lab filters are currently enabled before invoking it, ensuring
the disease-variant filter remains disabled when operating in environment-only
mode.
🧹 Nitpick comments (2)
sormas-backend/src/test/java/de/symeda/sormas/backend/dashboard/sample/SampleDashboardFacadeEjbTest.java (1)

391-434: ⚡ Quick win

Also validate lab filters through getTestResultCountsByResultType.

This method only verifies getSampleCountsByResultType, but this PR also changes lab-filter behavior in getTestResultCountsByResultType. Add one assertion path there to lock in both contracts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/dashboard/sample/SampleDashboardFacadeEjbTest.java`
around lines 391 - 434, The test currently only validates the
getSampleCountsByResultType method but does not verify the
getTestResultCountsByResultType method, which is also modified by this PR's
lab-filter behavior changes. Add assertion statements that call
getSampleDashboardFacade().getTestResultCountsByResultType() with the same
SampleDashboardCriteria filters (pathogenTestResult, serogroup, and
diseaseVariant) to ensure the lab-filter behavior is consistent and correctly
implemented in both methods.
sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java (1)

3472-3566: ⚡ Quick win

Add a deletion-state assertion for the new lab filters.

This test covers matching semantics well, but it doesn’t verify the new “exclude logically deleted sample/pathogen test” behavior. Adding one deleted-row assertion here would prevent regressions in the new EXISTS predicates.

Suggested addition
+		// Deleted sample/pathogen-test rows must not match filters.
+		SampleDto deletedSample = creator.createSample(
+			caseA.toReference(),
+			surveillanceSupervisor.toReference(),
+			rdcf.facility,
+			s -> s.setPathogenTestResult(PathogenTestResultType.POSITIVE));
+		PathogenTestDto deletedTest = creator.createPathogenTest(deletedSample.toReference(), surveillanceSupervisor.toReference(), t -> {
+			t.setTestedDisease(Disease.EVD);
+			t.setLab(rdcf.facility);
+			t.setSerotypeText("Deleted Serogroup");
+		});
+		getPathogenTestFacade().delete(deletedTest.getUuid(), new DeletionDetails(DeletionReason.OTHER_REASON, "test"));
+		getSampleFacade().delete(deletedSample.getUuid(), new DeletionDetails(DeletionReason.OTHER_REASON, "test"));
+
+		assertEquals(
+			0,
+			getCaseFacade()
+				.getIndexList(new CaseCriteria().pathogenTestResult(PathogenTestResultType.POSITIVE).serogroup("deleted"), 0, 100, null)
+				.size());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java`
around lines 3472 - 3566, The test method
testFilterCasesByTestResultAndSerogroup verifies the matching semantics of the
test result and serogroup filters but does not validate that logically deleted
samples and pathogen tests are properly excluded from the filter results. Add a
test case (similar to caseA, caseB, and caseC) that creates a case with a sample
or pathogen test that is marked as deleted, then apply the pathogenTestResult
and serogroup filters using getCaseFacade().getIndexList() with the new
CaseCriteria, and assert that the deleted row is not included in the results to
prevent regressions in the new EXISTS predicates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@sormas-ui/src/main/java/de/symeda/sormas/ui/dashboard/sample/SampleDashboardFilterLayout.java`:
- Around line 115-133: The call to
repopulateDiseaseVariantFilter(selectedDisease) at the end of the diseaseFilter
value change listener unconditionally repopulates the disease-variant combo box,
which can re-enable it after setLabFiltersEnabled(false) has disabled lab
filters for environment-only sample material. Modify the diseaseFilter value
change listener to make the repopulateDiseaseVariantFilter call conditional by
checking whether lab filters are currently enabled before invoking it, ensuring
the disease-variant filter remains disabled when operating in environment-only
mode.

---

Nitpick comments:
In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java`:
- Around line 3472-3566: The test method testFilterCasesByTestResultAndSerogroup
verifies the matching semantics of the test result and serogroup filters but
does not validate that logically deleted samples and pathogen tests are properly
excluded from the filter results. Add a test case (similar to caseA, caseB, and
caseC) that creates a case with a sample or pathogen test that is marked as
deleted, then apply the pathogenTestResult and serogroup filters using
getCaseFacade().getIndexList() with the new CaseCriteria, and assert that the
deleted row is not included in the results to prevent regressions in the new
EXISTS predicates.

In
`@sormas-backend/src/test/java/de/symeda/sormas/backend/dashboard/sample/SampleDashboardFacadeEjbTest.java`:
- Around line 391-434: The test currently only validates the
getSampleCountsByResultType method but does not verify the
getTestResultCountsByResultType method, which is also modified by this PR's
lab-filter behavior changes. Add assertion statements that call
getSampleDashboardFacade().getTestResultCountsByResultType() with the same
SampleDashboardCriteria filters (pathogenTestResult, serogroup, and
diseaseVariant) to ensure the lab-filter behavior is consistent and correctly
implemented in both methods.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d22c166d-9e95-48ea-99d9-df11e4d998f9

📥 Commits

Reviewing files that changed from the base of the PR and between 872d921 and 0805869.

📒 Files selected for processing (12)
  • sormas-api/src/main/java/de/symeda/sormas/api/caze/CaseCriteria.java
  • sormas-api/src/main/java/de/symeda/sormas/api/dashboard/SampleDashboardCriteria.java
  • sormas-api/src/main/java/de/symeda/sormas/api/i18n/Captions.java
  • sormas-api/src/main/resources/captions.properties
  • sormas-backend/src/main/java/de/symeda/sormas/backend/caze/CaseService.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/dashboard/sample/SampleDashboardService.java
  • sormas-backend/src/main/java/de/symeda/sormas/backend/sample/PathogenTest.java
  • sormas-backend/src/test/java/de/symeda/sormas/backend/caze/CaseFacadeEjbTest.java
  • sormas-backend/src/test/java/de/symeda/sormas/backend/dashboard/sample/SampleDashboardFacadeEjbTest.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/caze/CaseFilterForm.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/dashboard/sample/SampleDashboardDataProvider.java
  • sormas-ui/src/main/java/de/symeda/sormas/ui/dashboard/sample/SampleDashboardFilterLayout.java

@roldy
roldy requested review from KarnaiahPesula and raulbob June 17, 2026 10:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Laboratory - Filter by test result, serogroup, and variant

2 participants