Skip to content

Commit ffc95fe

Browse files
committed
#13772 - Add Epipulse export functionality for IPI disease
1 parent fee03b8 commit ffc95fe

File tree

2 files changed

+65
-56
lines changed

2 files changed

+65
-56
lines changed

sormas-backend/src/main/java/de/symeda/sormas/backend/epipulse/strategy/IpiCsvExportStrategy.java

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,28 @@
2525

2626
/**
2727
* CSV export strategy for PNEU (Invasive Pneumococcal Infection) disease.
28-
* Follows EpiPulse metadata specification exactly with 49 fixed columns (no repeatable fields).
28+
* Follows EpiPulse metadata specification with 49 fixed CSV columns (no repeatable fields).
2929
* <p>
30-
* Column structure:
31-
* - 13 common demographic fields
32-
* - 5 clinical/diagnostic fields (including DateOfDiagnosis, ClinicalCriteria)
30+
* CSV column structure:
31+
* - 13 common demographic fields (Disease through PlaceOfNotification)
32+
* - 5 clinical/diagnostic fields (CaseClassification, DateOfDiagnosis, DateOfNotification, Outcome, ClinicalCriteria)
3333
* - 2 laboratory fields (PathogenDetectionMethod, Serotype)
34-
* - 23 vaccination fields (detailed PCV1-4 and PPV tracking)
35-
* - 9 AST fields (antimicrobial susceptibility for CTX/CFX, ERY, PEN)
34+
* - 19 vaccination fields (DateOfLastVaccination, Vaccine, VaccinationStatus, PCV1-4 details, PPV details)
35+
* - 10 AST fields (ASTMethod, MIC/SIR for CTX_CFX, ERY, PEN)
3636
* <p>
37-
* Total: 49 fixed columns
37+
* Total: 49 CSV columns
38+
* <p>
39+
* Note: The underlying SQL query returns 56 columns (28 common + 28 PNEU-specific) which are
40+
* transformed during CSV export (e.g., symptom columns merged into ClinicalCriteria, dose flags
41+
* derived from dates).
3842
*/
3943
@Stateless
4044
@LocalBean
4145
public class IpiCsvExportStrategy implements CsvExportStrategy {
4246

4347
@Override
4448
public List<String> buildColumnNames(EpipulseDiseaseExportResult exportResult) {
45-
// PNEU has 49 fixed columns - no repeatable fields according to metadata
49+
// PNEU has 49 fixed CSV columns - no repeatable fields
4650
return List.of(
4751
// Common demographic fields (13)
4852
"Disease",
@@ -67,7 +71,7 @@ public List<String> buildColumnNames(EpipulseDiseaseExportResult exportResult) {
6771
// Laboratory fields (2)
6872
"PathogenDetectionMethod",
6973
"Serotype",
70-
// Vaccination fields (23)
74+
// Vaccination fields (19)
7175
"DateOfLastVaccination",
7276
"Vaccine",
7377
"VaccinationStatus",
@@ -87,7 +91,7 @@ public List<String> buildColumnNames(EpipulseDiseaseExportResult exportResult) {
8791
"DosePPV",
8892
"DatePPV",
8993
"PPVDoses",
90-
// AST fields (9)
94+
// AST fields (10)
9195
"ASTMethod",
9296
"MICSign_CTX_CFX",
9397
"MICValueAST_CTX_CFX",
@@ -130,7 +134,7 @@ public int writeEntryRow(EpipulseDiseaseExportEntryDto dto, String[] exportLine,
130134
exportLine[++index] = dto.getPathogenDetectionMethodForCsv();
131135
exportLine[++index] = dto.getSerotypeForCsv();
132136

133-
// Vaccination fields (23)
137+
// Vaccination fields (19)
134138
exportLine[++index] = dto.getDateOfLastVaccinationForCsv();
135139
exportLine[++index] = dto.getVaccineForCsv();
136140
exportLine[++index] = dto.getVaccinationStatusForCsv();
@@ -155,7 +159,7 @@ public int writeEntryRow(EpipulseDiseaseExportEntryDto dto, String[] exportLine,
155159
exportLine[++index] = dto.getDatePPVForCsv();
156160
exportLine[++index] = dto.getPpvDosesForCsv();
157161

158-
// AST fields (9)
162+
// AST fields (10)
159163
exportLine[++index] = dto.getAstMethodForCsv();
160164

161165
// CTX/CFX (Cefotaxime/Ceftriaxone)

sormas-backend/src/main/java/de/symeda/sormas/backend/epipulse/strategy/IpiExportStrategy.java

Lines changed: 49 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@
3030

3131
/**
3232
* Export strategy for PNEU (Invasive Pneumococcal Infection) disease exports.
33-
* PNEU follows the EpiPulse metadata specification exactly with 49 fixed columns (no repeatable fields).
33+
* PNEU follows the EpiPulse metadata specification with 56 total columns (28 common + 28 PNEU-specific).
3434
* <p>
35-
* Metadata specification:
36-
* - 13 common demographic fields
37-
* - 5 clinical/diagnostic fields (including DateOfDiagnosis, ClinicalCriteria)
38-
* - 2 laboratory fields (PathogenDetectionMethod, Serotype)
39-
* - 23 vaccination fields (detailed PCV1-4 and PPV tracking)
40-
* - 9 AST fields (antimicrobial susceptibility for CTX/CFX, ERY, PEN)
35+
* Column breakdown:
36+
* - 28 common fields (demographic, case identification, hospitalization, outcome)
37+
* - 28 PNEU-specific fields:
38+
* - 1 NRL field (NRLData)
39+
* - 5 clinical/diagnostic fields (DateOfDiagnosis, meningitis, septicaemia, pneumonia -> ClinicalCriteria)
40+
* - 2 laboratory fields (PathogenDetectionMethod, Serotype)
41+
* - 11 PCV vaccination fields (DatePCV1-4, BrandPCV1-4, DosePCV1-4 derived, PcvDoses)
42+
* - 2 PPV vaccination fields (DatePPV, PpvDoses)
43+
* - 10 AST fields (AstMethod, MicSign/MicValue/SIR for CTX_CFX, ERY, PEN)
4144
*/
4245
@Stateless
4346
@LocalBean
@@ -65,15 +68,15 @@ protected String buildDiseaseExportQuery() {
6568
// Note: AST data not currently stored in SORMAS for PNEU - fields will be NULL
6669
// query.append(buildPneuAstDataCte());
6770

68-
// Main SELECT clause with all 49 PNEU fields
71+
// Main SELECT clause with all 56 columns (28 common + 28 PNEU-specific)
6972
query.append(buildPneuSelectClause());
7073

7174
return query.toString();
7275
}
7376

7477
/**
75-
* Maps all 21 PNEU-specific fields from SQL result row to DTO
76-
* (Common fields already mapped by parent class - 28 fields)
78+
* Maps all 28 PNEU-specific fields from SQL result row to DTO.
79+
* Common fields (28) are already mapped by parent class, giving 56 total columns.
7780
*/
7881
@Override
7982
protected void mapDiseaseSpecificFields(EpipulseDiseaseExportEntryDto dto, Object[] row, int startIndex) {
@@ -206,8 +209,7 @@ protected void mapDiseaseSpecificFields(EpipulseDiseaseExportEntryDto dto, Objec
206209

207210
@Override
208211
protected void calculateDiseaseSpecificMaxCounts(List<EpipulseDiseaseExportEntryDto> entries, EpipulseDiseaseExportResult result) {
209-
// PNEU has no repeatable fields according to metadata specification
210-
// All 49 columns are fixed - no dynamic column generation needed
212+
// PNEU has no repeatable fields - all 56 columns are fixed (no dynamic column generation needed)
211213
// Common repeatable fields (pathogenTests, immunizations) are handled by parent class
212214
}
213215

@@ -269,36 +271,39 @@ private String buildPneuClinicalCriteriaCte() {
269271
*/
270272
private String buildPneuVaccinationDetailsCte() {
271273
//@formatter:off
272-
return "pneu_vaccination_details AS (" +
273-
" SELECT c.id as case_id," +
274-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 1 THEN v.vaccinationdate END) as date_pcv1," +
275-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 1 THEN CAST(v.vaccinename AS text) END) as brand_pcv1," +
276-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 2 THEN v.vaccinationdate END) as date_pcv2," +
277-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 2 THEN CAST(v.vaccinename AS text) END) as brand_pcv2," +
278-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 3 THEN v.vaccinationdate END) as date_pcv3," +
279-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 3 THEN CAST(v.vaccinename AS text) END) as brand_pcv3," +
280-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 4 THEN v.vaccinationdate END) as date_pcv4," +
281-
" MAX(CASE WHEN is_pcv AND pcv_row_num = 4 THEN CAST(v.vaccinename AS text) END) as brand_pcv4," +
282-
" SUM(CASE WHEN is_pcv THEN 1 ELSE 0 END) as pcv_doses," +
283-
" MAX(CASE WHEN is_ppv AND ppv_row_num = 1 THEN v.vaccinationdate END) as date_ppv," +
284-
" SUM(CASE WHEN is_ppv THEN 1 ELSE 0 END) as ppv_doses " +
285-
" FROM filtered_cases c " +
286-
" LEFT JOIN vaccination v ON v.immunization_id IN (SELECT i.id FROM immunization i WHERE i.person_id = c.person_id AND i.disease = 'INVASIVE_PNEUMOCOCCAL_INFECTION') " +
287-
" LEFT JOIN LATERAL (" +
288-
" SELECT CASE " +
289-
" WHEN CAST(v.vaccinename AS text) LIKE '%PCV%' OR CAST(v.vaccinename AS text) LIKE '%CONJUGATE%' THEN true " +
290-
" ELSE false " +
291-
" END as is_pcv," +
292-
" CASE " +
293-
" WHEN CAST(v.vaccinename AS text) LIKE '%PPV%' OR CAST(v.vaccinename AS text) LIKE '%POLYSACCHARIDE%' THEN true " +
294-
" ELSE false " +
295-
" END as is_ppv," +
296-
" SUM(CASE WHEN CAST(v.vaccinename AS text) LIKE '%PCV%' OR CAST(v.vaccinename AS text) LIKE '%CONJUGATE%' THEN 1 ELSE 0 END) " +
297-
" OVER (PARTITION BY c.id ORDER BY v.vaccinationdate) as pcv_row_num," +
298-
" SUM(CASE WHEN CAST(v.vaccinename AS text) LIKE '%PPV%' OR CAST(v.vaccinename AS text) LIKE '%POLYSACCHARIDE%' THEN 1 ELSE 0 END) " +
299-
" OVER (PARTITION BY c.id ORDER BY v.vaccinationdate) as ppv_row_num " +
300-
" ) vax_info ON true " +
301-
" GROUP BY c.id) ";
274+
return "pneu_vaccination_details AS (" +
275+
" SELECT vax.case_id," +
276+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 1 THEN vax.vaccinationdate END) as date_pcv1," +
277+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 1 THEN CAST(vax.vaccinename AS text) END) as brand_pcv1," +
278+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 2 THEN vax.vaccinationdate END) as date_pcv2," +
279+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 2 THEN CAST(vax.vaccinename AS text) END) as brand_pcv2," +
280+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 3 THEN vax.vaccinationdate END) as date_pcv3," +
281+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 3 THEN CAST(vax.vaccinename AS text) END) as brand_pcv3," +
282+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 4 THEN vax.vaccinationdate END) as date_pcv4," +
283+
" MAX(CASE WHEN vax.is_pcv AND vax.pcv_row_num = 4 THEN CAST(vax.vaccinename AS text) END) as brand_pcv4," +
284+
" SUM(CASE WHEN vax.is_pcv THEN 1 ELSE 0 END) as pcv_doses," +
285+
" MAX(CASE WHEN vax.is_ppv AND vax.ppv_row_num = 1 THEN vax.vaccinationdate END) as date_ppv," +
286+
" SUM(CASE WHEN vax.is_ppv THEN 1 ELSE 0 END) as ppv_doses " +
287+
" FROM (" +
288+
" SELECT c.id as case_id," +
289+
" v.vaccinationdate," +
290+
" v.vaccinename," +
291+
" CASE " +
292+
" WHEN CAST(v.vaccinename AS text) LIKE '%PCV%' OR CAST(v.vaccinename AS text) LIKE '%CONJUGATE%' THEN true " +
293+
" ELSE false " +
294+
" END as is_pcv," +
295+
" CASE " +
296+
" WHEN CAST(v.vaccinename AS text) LIKE '%PPV%' OR CAST(v.vaccinename AS text) LIKE '%POLYSACCHARIDE%' THEN true " +
297+
" ELSE false " +
298+
" END as is_ppv," +
299+
" SUM(CASE WHEN CAST(v.vaccinename AS text) LIKE '%PCV%' OR CAST(v.vaccinename AS text) LIKE '%CONJUGATE%' THEN 1 ELSE 0 END) " +
300+
" OVER (PARTITION BY c.id ORDER BY v.vaccinationdate) as pcv_row_num," +
301+
" SUM(CASE WHEN CAST(v.vaccinename AS text) LIKE '%PPV%' OR CAST(v.vaccinename AS text) LIKE '%POLYSACCHARIDE%' THEN 1 ELSE 0 END) " +
302+
" OVER (PARTITION BY c.id ORDER BY v.vaccinationdate) as ppv_row_num " +
303+
" FROM filtered_cases c " +
304+
" LEFT JOIN vaccination v ON v.immunization_id IN (SELECT i.id FROM immunization i WHERE i.person_id = c.person_id AND i.disease = 'INVASIVE_PNEUMOCOCCAL_INFECTION') " +
305+
" ) vax " +
306+
" GROUP BY vax.case_id) ";
302307
//@formatter:on
303308
}
304309

@@ -331,12 +336,12 @@ private String buildPneuAstDataCte() {
331336
}
332337

333338
/**
334-
* Builds SELECT clause with all 49 PNEU fields following metadata specification
339+
* Builds SELECT clause with all 56 columns (28 common + 28 PNEU-specific).
335340
*/
336341
private String buildPneuSelectClause() {
337342
StringBuilder select = new StringBuilder();
338343
//@formatter:off
339-
// Use common SELECT fields (28 fields) and append PNEU-specific fields (21 fields)
344+
// Use common SELECT fields (28 fields) and append PNEU-specific fields (28 fields)
340345
select.append("SELECT ")
341346
.append(sqlCteBuilder.buildCommonSelectFields())
342347
.append(",")

0 commit comments

Comments
 (0)