Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ Page<FeatureConfigurationIndexDto> getIndexPage(

void saveFeatureConfiguration(@Valid FeatureConfigurationIndexDto configuration, FeatureType featureType);

/**
* Enables or disables a server-level feature (one with no region/district/disease scope), seeding the
* configuration row if it does not exist yet. Intended for the administrator feature-configuration view.
*/
void setServerFeatureEnabled(FeatureType featureType, boolean enabled);

void deleteAllFeatureConfigurations(FeatureConfigurationCriteria criteria);

void deleteAllExpiredFeatureConfigurations(Date date);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@
*/
package de.symeda.sormas.api.feature;

import static de.symeda.sormas.api.common.DeletableEntityType.*;
import static de.symeda.sormas.api.common.DeletableEntityType.CASE;
import static de.symeda.sormas.api.common.DeletableEntityType.CONTACT;
import static de.symeda.sormas.api.common.DeletableEntityType.EVENT;
import static de.symeda.sormas.api.common.DeletableEntityType.EVENT_PARTICIPANT;
import static de.symeda.sormas.api.common.DeletableEntityType.IMMUNIZATION;
import static de.symeda.sormas.api.common.DeletableEntityType.TRAVEL_ENTRY;

import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.ImmutableMap;

Expand Down Expand Up @@ -57,6 +66,18 @@ public enum FeatureType {
SAMPLES_LAB },
null,
null),
SAMPLE_ADD_PATHOGEN_TEST(true,
true,
new FeatureType[] {
SAMPLES_LAB },
null,
null),
PATHOGEN_TEST_RESULT_REQUIRED(true,
true,
new FeatureType[] {
SAMPLES_LAB },
null,
null),
TASK_MANAGEMENT(true, true, null, null, ImmutableMap.of(FeatureTypeProperty.ALLOW_FREE_EDITING, Boolean.FALSE)),
WEEKLY_REPORTING(true, true, null, null, null),
IMMUNIZATION_MANAGEMENT(true, true, null, null, ImmutableMap.of(FeatureTypeProperty.REDUCED, Boolean.FALSE)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2078,6 +2078,7 @@ public interface Captions {
String facilityArchivedFacilities = "facilityArchivedFacilities";
String facilityType = "facilityType";
String facilityTypeGroup = "facilityTypeGroup";
String featureConfiguration = "featureConfiguration";
String FeatureConfiguration_districtName = "FeatureConfiguration.districtName";
String FeatureConfiguration_enabled = "FeatureConfiguration.enabled";
String FeatureConfiguration_endDate = "FeatureConfiguration.endDate";
Expand Down Expand Up @@ -2691,6 +2692,7 @@ public interface Captions {
String Sample_pathogenTestCount = "Sample.pathogenTestCount";
String Sample_pathogenTestingRequested = "Sample.pathogenTestingRequested";
String Sample_pathogenTestResult = "Sample.pathogenTestResult";
String Sample_performedByReferenceLaboratory = "Sample.performedByReferenceLaboratory";
String Sample_received = "Sample.received";
String Sample_receivedDate = "Sample.receivedDate";
String Sample_referredToUuid = "Sample.referredToUuid";
Expand All @@ -2704,6 +2706,7 @@ public interface Captions {
String Sample_requestedOtherPathogenTests = "Sample.requestedOtherPathogenTests";
String Sample_requestedPathogenTests = "Sample.requestedPathogenTests";
String Sample_requestedPathogenTestsTags = "Sample.requestedPathogenTestsTags";
String Sample_retestRequested = "Sample.retestRequested";
String Sample_sampleCode = "Sample.sampleCode";
String Sample_sampleDateTime = "Sample.sampleDateTime";
String Sample_sampleMaterial = "Sample.sampleMaterial";
Expand Down Expand Up @@ -3628,6 +3631,8 @@ public interface Captions {
String View_configuration_emailTemplates_short = "View.configuration.emailTemplates.short";
String View_configuration_facilities = "View.configuration.facilities";
String View_configuration_facilities_short = "View.configuration.facilities.short";
String View_configuration_featureConfiguration = "View.configuration.featureConfiguration";
String View_configuration_featureConfiguration_short = "View.configuration.featureConfiguration.short";
String View_configuration_laboratories = "View.configuration.laboratories";
String View_configuration_laboratories_short = "View.configuration.laboratories.short";
String View_configuration_linelisting = "View.configuration.linelisting";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ public interface Strings {
String headingExternalMessagesDeleted = "headingExternalMessagesDeleted";
String headingExternalMessagesNotDeleted = "headingExternalMessagesNotDeleted";
String headingFatalities = "headingFatalities";
String headingFeatureConfiguration = "headingFeatureConfiguration";
String headingFetchExternalMessages = "headingFetchExternalMessages";
String headingFileExists = "headingFileExists";
String headingFilters = "headingFilters";
Expand Down Expand Up @@ -1069,6 +1070,7 @@ public interface Strings {
String infoExternalMessageHospitalizationMissingHospital = "infoExternalMessageHospitalizationMissingHospital";
String infoFacilityCsvImport = "infoFacilityCsvImport";
String infoFacilityNeedsDistrict = "infoFacilityNeedsDistrict";
String infoFeatureConfiguration = "infoFeatureConfiguration";
String infoHeadingAefiDashboardMap = "infoHeadingAefiDashboardMap";
String infoHeadingEnvironmentSampleDashboardMap = "infoHeadingEnvironmentSampleDashboardMap";
String infoHeadingSampleDashboardMap = "infoHeadingSampleDashboardMap";
Expand Down Expand Up @@ -1510,6 +1512,7 @@ public interface Strings {
String messageFacilityDearchived = "messageFacilityDearchived";
String messageFacilityDearchivingNotPossible = "messageFacilityDearchivingNotPossible";
String messageFacilityMulitChanged = "messageFacilityMulitChanged";
String messageFeatureConfigurationSaved = "messageFeatureConfigurationSaved";
String messageFollowUpCanceled = "messageFollowUpCanceled";
String messageFollowUpStatusChanged = "messageFollowUpStatusChanged";
String messageForwardedExternalMessageFound = "messageForwardedExternalMessageFound";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class SampleDto extends SormasToSormasShareableDto implements IsSample {
public static final String SAMPLING_REASON_DETAILS = "samplingReasonDetails";
public static final String DELETION_REASON = "deletionReason";
public static final String OTHER_DELETION_REASON = "otherDeletionReason";
public static final String PERFORMED_BY_REFERENCE_LABORATORY = "performedByReferenceLaboratory";
public static final String RETEST_REQUESTED = "retestRequested";

private CaseReferenceDto associatedCase;
private ContactReferenceDto associatedContact;
Expand Down Expand Up @@ -135,6 +137,8 @@ public class SampleDto extends SormasToSormasShareableDto implements IsSample {

private Boolean pathogenTestingRequested;
private Boolean additionalTestingRequested;
private Boolean performedByReferenceLaboratory;
private Boolean retestRequested = Boolean.FALSE;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
private Set<PathogenTestType> requestedPathogenTests;
private Set<AdditionalTestType> requestedAdditionalTests;
@Size(max = FieldConstraints.CHARACTER_LIMIT_DEFAULT, message = Validations.textTooLong)
Expand Down Expand Up @@ -368,6 +372,22 @@ public void setAdditionalTestingRequested(Boolean additionalTestingRequested) {
this.additionalTestingRequested = additionalTestingRequested;
}

public Boolean getPerformedByReferenceLaboratory() {
return performedByReferenceLaboratory;
}

public void setPerformedByReferenceLaboratory(Boolean performedByReferenceLaboratory) {
this.performedByReferenceLaboratory = performedByReferenceLaboratory;
}

public Boolean getRetestRequested() {
return retestRequested;
}

public void setRetestRequested(Boolean retestRequested) {
this.retestRequested = retestRequested;
}

@ImportIgnore
public Set<PathogenTestType> getRequestedPathogenTests() {
return requestedPathogenTests;
Expand Down Expand Up @@ -489,6 +509,8 @@ private static void migrateAttributesOfPhysicalSample(SampleDto source, SampleDt
target.setSampleSource(source.getSampleSource());
target.setPathogenTestingRequested(source.getPathogenTestingRequested());
target.setAdditionalTestingRequested(source.getAdditionalTestingRequested());
target.setPerformedByReferenceLaboratory(source.getPerformedByReferenceLaboratory());
target.setRetestRequested(source.getRetestRequested());
target.setRequestedPathogenTests(source.getRequestedPathogenTests());
target.setRequestedAdditionalTests(source.getRequestedAdditionalTests());
target.setFieldSampleID(source.getFieldSampleID());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public class SampleExportDto extends AbstractUuidDto implements IsSample {
private Boolean additionalTestingRequested;
private Set<AdditionalTestType> requestedAdditionalTests;
private String requestedOtherAdditionalTests;
private Boolean performedByReferenceLaboratory;
private Boolean retestRequested;
private boolean shipped;
private Date shipmentDate;
@SensitiveData
Expand Down Expand Up @@ -159,6 +161,7 @@ public SampleExportDto(long id, String uuid, String labSampleId, Date sampleRepo
String contactRegion, String contactDistrict, String contactCommunity, String contactCaseRegion, String contactCaseDistrict, String contactCaseCommunity,
Date contactReportDate, Date lastContactDate, ContactClassification contactClassification, ContactStatus contactStatus, String eventParticipantRegion, String eventParticipantDistrict,
String labUuid, String caseHealthFacilityUuid, String caseResponsibleRegion, String caseResponsibleDistrict, String caseResponsibleCommunity,
Boolean performedByReferenceLaboratory, Boolean retestRequested,
boolean isInJurisdiction, boolean isCaseInJurisdiction, boolean isContactInJurisdiction, boolean isContactCaseInJurisdiction, boolean isEventParticipantInJurisdiction) {
//@formatter:on
super(uuid);
Expand Down Expand Up @@ -231,6 +234,8 @@ public SampleExportDto(long id, String uuid, String labSampleId, Date sampleRepo
}
}
this.requestedOtherAdditionalTests = requestedOtherAdditionalTests;
this.performedByReferenceLaboratory = performedByReferenceLaboratory;
this.retestRequested = retestRequested;
this.shipped = shipped;
this.shipmentDate = shipmentDate;
this.shipmentDetails = shipmentDetails;
Expand Down Expand Up @@ -858,6 +863,24 @@ public String getSampleMaterialSnomedCode() {
return material != null ? material.getSnomedCode() : null;
}

@Order(105)
public Boolean getPerformedByReferenceLaboratory() {
return performedByReferenceLaboratory;
}

public void setPerformedByReferenceLaboratory(Boolean performedByReferenceLaboratory) {
this.performedByReferenceLaboratory = performedByReferenceLaboratory;
}

@Order(106)
public Boolean getRetestRequested() {
return retestRequested;
}

public void setRetestRequested(Boolean retestRequested) {
this.retestRequested = retestRequested;
}

public SampleExportPathogenTest getPathogenTest1() {
return pathogenTest1;
}
Expand Down
5 changes: 5 additions & 0 deletions sormas-api/src/main/resources/captions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ epiWeekFrom=From Epi Week
epiWeekTo=To Epi Week
facilityType=Facility type
facilityTypeGroup=Facility category
featureConfiguration=Feature Configuration
firstName=First name
sex=Sex
nationalHealthId=National health ID
Expand Down Expand Up @@ -2285,6 +2286,7 @@ Sample.otherLab=Referral laboratory
Sample.pathogenTestingRequested=Request pathogen tests to be performed?
Sample.pathogenTestCount=Number of tests
Sample.pathogenTestResult=Final laboratory result
Sample.performedByReferenceLaboratory=Performed by reference laboratory
Sample.received=Received
Sample.receivedDate=Date sample received at lab
Sample.referredToUuid=Sample referred to
Expand All @@ -2297,6 +2299,7 @@ Sample.requestedOtherAdditionalTests=Other requested additional tests
Sample.requestedOtherPathogenTests=Other requested pathogen tests
Sample.requestedPathogenTests=If you wish to request specific additional tests, tick each of them in the list below
Sample.requestedPathogenTestsTags=Requested pathogen tests:
Sample.retestRequested=Retest requested
Sample.sampleCode=Sample code
Sample.sampleDateTime=Date sample was collected
Sample.sampleMaterial=Type of sample
Expand Down Expand Up @@ -3360,6 +3363,8 @@ View.configuration.documentTemplates=Document Templates Management
View.configuration.documentTemplates.short=Document Templates
View.configuration.facilities=Facilities Configuration
View.configuration.facilities.short=Facilities
View.configuration.featureConfiguration=Feature Configuration
View.configuration.featureConfiguration.short=Features
View.configuration.laboratories=Laboratories Configuration
View.configuration.laboratories.short=Laboratories
View.configuration.pointsofentry=Points of Entry Configuration
Expand Down
4 changes: 4 additions & 0 deletions sormas-api/src/main/resources/enum.properties
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,10 @@ FacilityType.CARE_RECIPIENT_HABITATION = Care recipient habitation
FacilityType.VISITING_AMBULATORY_AID = Visiting ambulatory aid
FacilityType.AFTER_SCHOOL = After school facility

#FeatureType
FeatureType.SAMPLE_ADD_PATHOGEN_TEST=Allow adding pathogen tests from a sample
FeatureType.PATHOGEN_TEST_RESULT_REQUIRED=Require a result when entering a pathogen test

#FacilityTypeGroup
FacilityTypeGroup.ACCOMMODATION=Accommodation
FacilityTypeGroup.CARE_FACILITY=Care facility
Expand Down
3 changes: 3 additions & 0 deletions sormas-api/src/main/resources/strings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ headingEventsNotDeleted = None of the events were deleted
headingEventsNotRestored = None of the events were restored
headingExportFailed = Export failed
headingFatalities=Fatalities
headingFeatureConfiguration = Feature Configuration
headingFileExists = Duplicate File
headingFilters = Filters
headingStoppedFollowUp = Stopped Follow-up
Expand Down Expand Up @@ -1016,6 +1017,7 @@ infoExpectedFollowUpUntilDateContact = The expected follow-up until date for thi
infoExportNoFilters = <b>Warning:</b> No filters have been selected. Export may take a while.
infoFacilityCsvImport = Name of a configured facility (requires FacilityType), OTHER_FACILITY (requires FacilityType and FacilityDetails) or NO_FACILITY
infoFacilityNeedsDistrict = Please define a district in order to select a facility.
infoFeatureConfiguration = Enable or disable optional system features. Changes take effect immediately and apply to the whole server.
infoImmunizationPeriod = Immunization period of this immunization
infoImmunizationStatusAcquired = Only immunizations with acquired status are considered
infoImmunizationValidFromClosest = Selects the immunization with the valid from date closest to (but not after) the case report date
Expand Down Expand Up @@ -1361,6 +1363,7 @@ messageCountEventsNotDeletedSormasToSormasReason = %s events not deleted because
messageExportFailed = There was an error preventing the data to be exported. Please contact an admin and inform them about this issue.
messageExportConfigurationDeleted = Export configuration deleted
messageExportConfigurationSaved = Export configuration saved
messageFeatureConfigurationSaved = Feature configuration saved
messageFollowUpCanceled = Follow-up of all selected contacts has been canceled
messageFollowUpStatusChanged = Follow-up of all selected contacts that have follow-up has been set to lost to follow-up
messageFacilityChanged = You have changed the facility of this case. Do you want to transfer the case to the new facility (the hospitalization may be updated) or do you only want to edit the data to correct a mistake?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,26 @@ public void saveFeatureConfiguration(@Valid FeatureConfigurationIndexDto configu
service.ensurePersisted(entity);
}

@Override
public void setServerFeatureEnabled(FeatureType featureType, boolean enabled) {

if (!featureType.isServerFeature()) {
throw new IllegalArgumentException("FeatureType " + featureType + " is not a server feature and cannot be toggled here.");
}

List<FeatureConfiguration> configurations = service.getServerFeatureConfigurationsByType(featureType);
if (configurations.isEmpty()) {
FeatureConfiguration configuration = FeatureConfiguration.build(featureType, enabled);
configuration.setProperties(featureType.getSupportedPropertyDefaults());
service.ensurePersisted(configuration);
} else {
for (FeatureConfiguration configuration : configurations) {
configuration.setEnabled(enabled);
service.ensurePersisted(configuration);
}
}
}

@Override
public void deleteAllFeatureConfigurations(FeatureConfigurationCriteria criteria) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.symeda.sormas.backend.feature;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
Expand All @@ -16,6 +17,7 @@
import javax.persistence.criteria.From;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
Expand Down Expand Up @@ -201,6 +203,21 @@ private Map<FeatureType, FeatureConfiguration> getServerFeatureConfigurations()
.collect(Collectors.toMap(FeatureConfiguration::getFeatureType, Function.identity(), (e1, e2) -> e2));
}

public FeatureConfiguration getServerFeatureConfiguration(FeatureType featureType) {
return getServerFeatureConfigurations().get(featureType);
}

public List<FeatureConfiguration> getServerFeatureConfigurationsByType(FeatureType featureType) {
if (!featureType.isServerFeature()) {
return new ArrayList<>();
}
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<FeatureConfiguration> cq = cb.createQuery(FeatureConfiguration.class);
Root<FeatureConfiguration> root = cq.from(FeatureConfiguration.class);
cq.where(cb.equal(root.get(FeatureConfiguration.FEATURE_TYPE), featureType));
return em.createQuery(cq).getResultList();
}

private boolean hasEnabledDependentFeature(FeatureType featureType, Map<FeatureType, FeatureConfiguration> featureConfigurationMap) {

for (FeatureType dependentFeatureType : featureType.getDependentFeatures()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import de.symeda.sormas.api.common.DeletionDetails;
import de.symeda.sormas.api.common.Page;
import de.symeda.sormas.api.environment.environmentsample.EnvironmentSampleReferenceDto;
import de.symeda.sormas.api.feature.FeatureType;
import de.symeda.sormas.api.i18n.I18nProperties;
import de.symeda.sormas.api.i18n.Validations;
import de.symeda.sormas.api.sample.PathogenTestCriteria;
Expand Down Expand Up @@ -78,6 +79,7 @@
import de.symeda.sormas.backend.event.EventFacadeEjb.EventFacadeEjbLocal;
import de.symeda.sormas.backend.event.EventParticipant;
import de.symeda.sormas.backend.event.EventParticipantFacadeEjb.EventParticipantFacadeEjbLocal;
import de.symeda.sormas.backend.feature.FeatureConfigurationFacadeEjb.FeatureConfigurationFacadeEjbLocal;
import de.symeda.sormas.backend.infrastructure.country.CountryFacadeEjb;
import de.symeda.sormas.backend.infrastructure.country.CountryService;
import de.symeda.sormas.backend.infrastructure.facility.FacilityFacadeEjb;
Expand Down Expand Up @@ -132,6 +134,8 @@ public class PathogenTestFacadeEjb implements PathogenTestFacade {
private ConfigFacadeEjbLocal configFacade;
@EJB
private DrugSusceptibilityMapper drugSusceptibilityMapper;
@EJB
private FeatureConfigurationFacadeEjbLocal featureConfigurationFacade;

@Override
public List<String> getAllActiveUuids() {
Expand Down Expand Up @@ -435,6 +439,13 @@ public PathogenTestDto savePathogenTest(@Valid PathogenTestDto dto, boolean chec

restorePseudonymizedDto(dto, existingSampleTest, existingSampleTestDto);

// When the administrator has not made the result mandatory (#13948 issue #13958), an empty result is
// allowed; default it to PENDING so the non-null DB constraint holds. This covers every save path
// (UI, environment samples, external-message processing, REST), not just the human-sample UI flow.
if (dto.getTestResult() == null && !featureConfigurationFacade.isFeatureEnabled(FeatureType.PATHOGEN_TEST_RESULT_REQUIRED)) {
dto.setTestResult(PathogenTestResultType.PENDING);
}

validate(dto);

PathogenTest pathogenTest = fillOrBuildEntity(dto, existingSampleTest, checkChangeDate);
Expand Down
Loading
Loading