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 @@ -32,6 +32,7 @@
import de.symeda.sormas.api.exposure.ModeOfTransmission;
import de.symeda.sormas.api.feature.FeatureType;
import de.symeda.sormas.api.i18n.Validations;
import de.symeda.sormas.api.infrastructure.country.CountryReferenceDto;
import de.symeda.sormas.api.utils.DataHelper;
import de.symeda.sormas.api.utils.DependingOnFeatureType;
import de.symeda.sormas.api.utils.Diseases;
Expand Down Expand Up @@ -65,17 +66,22 @@ public class EpiDataDto extends PseudonymizableDto {
public static final String MODE_OF_TRANSMISSION_TYPE = "modeOfTransmissionType";
public static final String INFECTION_SOURCE = "infectionSource";
public static final String INFECTION_SOURCE_TEXT = "infectionSourceText";
public static final String IMPORTED_CASE = "importedCase";
public static final String COUNTRY = "country";

private YesNoUnknown exposureDetailsKnown;
private YesNoUnknown activityAsCaseDetailsKnown;
private YesNoUnknown contactWithSourceCaseKnown;
private YesNoUnknown highTransmissionRiskArea;
private YesNoUnknown largeOutbreaksArea;
@Diseases({
Disease.MEASLES,
Disease.GIARDIASIS })
Disease.MEASLES })
private CaseImportedStatus caseImportedStatus;
Comment on lines 77 to 79
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the removal of GIARDIASIS from caseImportedStatus.

The @Diseases annotation on caseImportedStatus now only includes Disease.MEASLES, with Disease.GIARDIASIS removed. This change aligns with the introduction of the new importedCase field for GIARDIASIS (lines 81-83). However, if existing GIARDIASIS cases previously used caseImportedStatus, this could be a breaking change.

Please confirm:

  1. Are there existing GIARDIASIS cases in production that use caseImportedStatus?
  2. If yes, is there a data migration strategy to move this data to the new importedCase field?
  3. Should the database schema changes include a migration script to handle this transition?

You can verify existing usage with:


🏁 Script executed:

#!/bin/bash
# Search for GIARDIASIS and caseImportedStatus usage together in the codebase
rg -n --type=java -C3 'caseImportedStatus' | grep -i giardiasis

# Check if there are any database migration scripts for this change
fd -e sql -e xml | xargs rg -l 'caseImportedStatus\|importedCase' | head -20

Length of output: 96


Remove stale GIARDIASIS entry and add migration

  • EpiDataDto.java (lines 77–83): @Diseases still includes Disease.GIARDIASIS—remove it to match intended MEASLES-only usage.
  • No SQL/XML migration found for moving existing GIARDIASIS data to the new importedCase field—add a DB migration script to preserve and transfer that data.
🤖 Prompt for AI Agents
In sormas-api/src/main/java/de/symeda/sormas/api/epidata/EpiDataDto.java around
lines 77 to 79, remove the stale Disease.GIARDIASIS entry from the @Diseases
annotation so it only lists Disease.MEASLES, and create a DB migration (SQL or
XML Flyway/Liquibase script) that finds rows still marked with GIARDIASIS in the
old field, maps those values to the new importedCase field for the corresponding
records, and clears or deprecates the old field; ensure the migration is
idempotent, covers all affected tables/columns, includes a short descriptive
versioned filename and test/verification steps in the migration comment.


@Diseases({
Disease.GIARDIASIS })
private YesNoUnknown importedCase;

@HideForCountriesExcept(countries = {
CountryHelper.COUNTRY_CODE_LUXEMBOURG })
@Diseases({
Expand Down Expand Up @@ -124,6 +130,10 @@ public class EpiDataDto extends PseudonymizableDto {
Disease.CRYPTOSPORIDIOSIS })
private String infectionSourceText;

@Diseases({
Disease.GIARDIASIS })
private CountryReferenceDto country;

@Valid
private List<ExposureDto> exposures = new ArrayList<>();

Expand Down Expand Up @@ -267,6 +277,22 @@ public void setModeOfTransmissionType(String modeOfTransmissionType) {
this.modeOfTransmissionType = modeOfTransmissionType;
}

public YesNoUnknown getImportedCase() {
return importedCase;
}

public void setImportedCase(YesNoUnknown importedCase) {
this.importedCase = importedCase;
}

public CountryReferenceDto getCountry() {
return country;
}

public void setCountry(CountryReferenceDto country) {
this.country = country;
}

@Override
public EpiDataDto clone() throws CloneNotSupportedException {
EpiDataDto clone = (EpiDataDto) super.clone();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1566,9 +1566,11 @@ public interface Captions {
String EpiData_clusterType = "EpiData.clusterType";
String EpiData_clusterTypeText = "EpiData.clusterTypeText";
String EpiData_contactWithSourceCaseKnown = "EpiData.contactWithSourceCaseKnown";
String EpiData_country = "EpiData.country";
String EpiData_exposureDetailsKnown = "EpiData.exposureDetailsKnown";
String EpiData_exposures = "EpiData.exposures";
String EpiData_highTransmissionRiskArea = "EpiData.highTransmissionRiskArea";
String EpiData_importedCase = "EpiData.importedCase";
String EpiData_infectionSource = "EpiData.infectionSource";
String EpiData_infectionSourceText = "EpiData.infectionSourceText";
String EpiData_largeOutbreaksArea = "EpiData.largeOutbreaksArea";
Expand Down
2 changes: 2 additions & 0 deletions sormas-api/src/main/resources/captions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,8 @@ EpiData.modeOfTransmission=Mode of transmission
EpiData.modeOfTransmissionType= Specify mode of transmission
EpiData.infectionSource= Suspected vehicle or source of infection
EpiData.infectionSourceText= Specify source of infection
EpiData.importedCase= Imported Case
EpiData.country= Country of contamination
#Therapy
Therapy.directlyObservedTreatment = Directly observed treatment
Therapy.mdrXdrTuberculosis = MDR/XDR tuberculosis
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import de.symeda.sormas.api.epidata.CaseImportedStatus;
Expand All @@ -37,6 +39,7 @@
import de.symeda.sormas.backend.common.AbstractDomainObject;
import de.symeda.sormas.backend.common.NotExposedToApi;
import de.symeda.sormas.backend.exposure.Exposure;
import de.symeda.sormas.backend.infrastructure.country.Country;

@Entity
public class EpiData extends AbstractDomainObject {
Expand All @@ -56,6 +59,7 @@ public class EpiData extends AbstractDomainObject {
private YesNoUnknown largeOutbreaksArea;
private YesNoUnknown areaInfectedAnimals;

private YesNoUnknown importedCase;
private CaseImportedStatus caseImportedStatus;
private ClusterType clusterType;
private String clusterTypeText;
Expand All @@ -67,6 +71,8 @@ public class EpiData extends AbstractDomainObject {
private ModeOfTransmission modeOfTransmission;
private String modeOfTransmissionType;

private Country country;

private List<Exposure> exposures = new ArrayList<>();
private List<ActivityAsCase> activitiesAsCase = new ArrayList<>();
@NotExposedToApi
Expand Down Expand Up @@ -223,4 +229,22 @@ public String getInfectionSourceText() {
public void setInfectionSourceText(String infectionSourceText) {
this.infectionSourceText = infectionSourceText;
}

@Enumerated(EnumType.STRING)
public YesNoUnknown getImportedCase() {
return importedCase;
}

public void setImportedCase(YesNoUnknown importedCase) {
this.importedCase = importedCase;
}

@ManyToOne(fetch = FetchType.LAZY)
public Country getCountry() {
return country;
}

public void setCountry(Country country) {
this.country = country;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import de.symeda.sormas.backend.contact.ContactService;
import de.symeda.sormas.backend.exposure.Exposure;
import de.symeda.sormas.backend.exposure.ExposureService;
import de.symeda.sormas.backend.infrastructure.country.CountryFacadeEjb;
import de.symeda.sormas.backend.infrastructure.country.CountryService;
import de.symeda.sormas.backend.location.LocationFacadeEjb;
import de.symeda.sormas.backend.location.LocationFacadeEjb.LocationFacadeEjbLocal;
import de.symeda.sormas.backend.user.UserFacadeEjb;
Expand All @@ -58,6 +60,8 @@ public class EpiDataFacadeEjb implements EpiDataFacade {
private ContactService contactService;
@EJB
private UserService userService;
@EJB
private CountryService countryService;

public EpiData fillOrBuildEntity(EpiDataDto source, EpiData target, boolean checkChangeDate) {
if (source == null) {
Expand Down Expand Up @@ -108,6 +112,8 @@ public EpiData fillOrBuildEntity(EpiDataDto source, EpiData target, boolean chec
target.setModeOfTransmissionType(source.getModeOfTransmissionType());
target.setInfectionSource(source.getInfectionSource());
target.setInfectionSourceText(source.getInfectionSourceText());
target.setCountry(countryService.getByReferenceDto(source.getCountry()));
target.setImportedCase(source.getImportedCase());

return target;
}
Expand Down Expand Up @@ -272,7 +278,8 @@ public static EpiDataDto toDto(EpiData epiData) {
target.setModeOfTransmissionType(source.getModeOfTransmissionType());
target.setInfectionSource(source.getInfectionSource());
target.setInfectionSourceText(source.getInfectionSourceText());

target.setCountry(CountryFacadeEjb.toReferenceDto(source.getCountry()));
target.setImportedCase(source.getImportedCase());
return target;
}

Expand Down
7 changes: 6 additions & 1 deletion sormas-backend/src/main/resources/sql/sormas_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -14665,6 +14665,7 @@ alter table symptoms add column if not exists symptomCurrentStatus varchar(255);
alter table symptoms add column if not exists durationOfSymptoms integer;
alter table symptoms add column if not exists overnightStayRequired varchar(255);
alter table symptoms add column if not exists bloating varchar(255);
ALTER TABLE symptoms ALTER COLUMN weightlossamount TYPE float4 USING weightlossamount::float4;
alter table exposures add column if not exists travelAccommodation varchar(255);
alter table exposures add column if not exists travelAccommodationType varchar(255);
alter table exposures add column if not exists swimmingLocation varchar(255);
Expand All @@ -14677,11 +14678,12 @@ alter table exposures add column if not exists rawfoodcontact varchar(255);
alter table exposures add column if not exists rawfoodcontacttext varchar(255);
alter table exposures add column if not exists symptomaticindividualtext varchar(255);
ALTER TABLE hospitalization ADD COLUMN IF NOT EXISTS durationOfHospitalization integer;

alter table epidata add column if not exists modeOfTransmission varchar(255);
alter table epidata add column if not exists modeOfTransmissionType varchar(255);
alter table epidata add column if not exists infectionSource varchar(255);
alter table epidata add column if not exists infectionSourcetext varchar(255);
alter table epidata add column if not exists importedCase varchar(255);
alter table epidata add column if not exists country_id bigint;
Comment on lines +14685 to +14686
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don’t re-create an existing FK with IF NOT EXISTS.

epidata_country_id_fk already exists in deployed databases. Re-running this script now tries to add the same foreign key again and Postgres errors because ALTER TABLE … ADD CONSTRAINT … IF NOT EXISTS isn’t supported. Drop-and-recreate or wrap the new constraint in a migration that first checks pg_constraint; otherwise the migration blocks releases.

🤖 Prompt for AI Agents
sormas-backend/src/main/resources/sql/sormas_schema.sql around lines
14685-14686: the migration blindly recreates the epidata_country_id_fk foreign
key which already exists and Postgres does not support ALTER TABLE ... ADD
CONSTRAINT IF NOT EXISTS; change the migration so it does not attempt to add the
FK unconditionally — either write a proper migration that first checks
pg_constraint (select to see if epidata_country_id_fk exists) and only issues
the ALTER TABLE ... ADD CONSTRAINT when absent, or perform a safe
drop-and-recreate: in a transactional migration drop the existing
epidata_country_id_fk if present and then add the new constraint; ensure the
check/drop logic runs in SQL (or the project’s migration framework) so
re-applying the script won’t error and will not block releases.

ALTER TABLE person ADD COLUMN IF NOT EXISTS workplace varchar(255);
ALTER TABLE person ADD COLUMN IF NOT EXISTS workplacetext varchar(255);

Expand All @@ -14693,6 +14695,7 @@ alter table symptoms_history add column if not exists symptomCurrentStatus varch
alter table symptoms_history add column if not exists durationOfSymptoms integer;
alter table symptoms_history add column if not exists overnightStayRequired varchar(255);
alter table symptoms_history add column if not exists bloating varchar(255);
ALTER TABLE symptoms_history ALTER COLUMN weightlossamount TYPE float4 USING weightlossamount::float4;
alter table exposures_history add column if not exists travelAccommodation varchar(255);
alter table exposures_history add column if not exists travelAccommodationType varchar(255);
alter table exposures_history add column if not exists swimmingLocation varchar(255);
Expand All @@ -14705,6 +14708,8 @@ alter table epidata_history add column if not exists modeOfTransmission varchar(
alter table epidata_history add column if not exists modeOfTransmissionType varchar(255);
alter table epidata_history add column if not exists infectionSource varchar(255);
alter table epidata_history add column if not exists infectionSourcetext varchar(255);
alter table epidata_history add column if not exists importedCase varchar(255);
alter table epidata_history add column if not exists country_id bigint;
alter table exposures_history add column if not exists rawfoodcontact varchar(255);
alter table exposures_history add column if not exists rawfoodcontacttext varchar(255);
alter table exposures_history add column if not exists symptomaticindividualtext varchar(255);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.commons.collections4.CollectionUtils;

import com.vaadin.shared.ui.ContentMode;
import com.vaadin.v7.ui.ComboBox;
import com.vaadin.v7.ui.Field;
import com.vaadin.v7.ui.TextField;

Expand All @@ -50,6 +51,7 @@
import de.symeda.sormas.api.exposure.ModeOfTransmission;
import de.symeda.sormas.api.i18n.I18nProperties;
import de.symeda.sormas.api.i18n.Strings;
import de.symeda.sormas.api.infrastructure.country.CountryReferenceDto;
import de.symeda.sormas.api.utils.YesNoUnknown;
import de.symeda.sormas.api.utils.fieldaccess.UiFieldAccessCheckers;
import de.symeda.sormas.api.utils.fieldvisibility.FieldVisibilityCheckers;
Expand Down Expand Up @@ -80,6 +82,7 @@ public class EpiDataForm extends AbstractEditForm<EpiDataDto> {
loc(EpiDataDto.EXPOSURES) +
loc(LOC_CONCLUSION_HEADING) +
fluidRowLocs(6,EpiDataDto.CASE_IMPORTED_STATUS,6,"") +
fluidRowLocs(6, EpiDataDto.IMPORTED_CASE, 6, EpiDataDto.COUNTRY)+
fluidRowLocs(EpiDataDto.MODE_OF_TRANSMISSION, EpiDataDto.MODE_OF_TRANSMISSION_TYPE) +
fluidRowLocs(EpiDataDto.INFECTION_SOURCE, EpiDataDto.INFECTION_SOURCE_TEXT) +
loc(LOC_ACTIVITY_AS_CASE_INVESTIGATION_HEADING) +
Expand Down Expand Up @@ -169,11 +172,15 @@ protected void addFields() {
addField(EpiDataDto.MODE_OF_TRANSMISSION_TYPE);
addField(EpiDataDto.INFECTION_SOURCE);
addField(EpiDataDto.INFECTION_SOURCE_TEXT);
addField(EpiDataDto.IMPORTED_CASE, NullableOptionGroup.class);
List<CountryReferenceDto> countries = FacadeProvider.getCountryFacade().getAllActiveAsReference();
ComboBox country = addInfrastructureField(EpiDataDto.COUNTRY);
country.addItems(countries);

TextField clustorTypeTF = addField(EpiDataDto.CLUSTER_TYPE_TEXT);
TextField clusterTypeTF = addField(EpiDataDto.CLUSTER_TYPE_TEXT);
FieldHelper
.setVisibleWhen(getFieldGroup(), EpiDataDto.CLUSTER_TYPE, EpiDataDto.CLUSTER_RELATED, Collections.singletonList(Boolean.TRUE), true);
FieldHelper.setVisibleWhen(getField(EpiDataDto.CLUSTER_TYPE), Arrays.asList(clustorTypeTF), Arrays.asList(ClusterType.OTHER), true);
FieldHelper.setVisibleWhen(getField(EpiDataDto.CLUSTER_TYPE), Arrays.asList(clusterTypeTF), Arrays.asList(ClusterType.OTHER), true);
FieldHelper.setVisibleWhen(
getFieldGroup(),
EpiDataDto.EXPOSURES,
Expand All @@ -183,6 +190,7 @@ protected void addFields() {
FieldHelper
.setVisibleWhen(getFieldGroup(), EpiDataDto.MODE_OF_TRANSMISSION_TYPE, EpiDataDto.MODE_OF_TRANSMISSION, ModeOfTransmission.OTHER, true);
FieldHelper.setVisibleWhen(getFieldGroup(), EpiDataDto.INFECTION_SOURCE_TEXT, EpiDataDto.INFECTION_SOURCE, InfectionSource.OTHER, true);
FieldHelper.setVisibleWhen(getFieldGroup(), EpiDataDto.COUNTRY, EpiDataDto.IMPORTED_CASE, YesNoUnknown.YES, true);
initializeVisibilitiesAndAllowedVisibilities();
initializeAccessAndAllowedAccesses();

Expand Down
Loading