Skip to content
This repository was archived by the owner on May 5, 2021. It is now read-only.

Commit a3e2173

Browse files
SORMAS-Foundation#2993 add country importer
1 parent bc9f4ae commit a3e2173

7 files changed

Lines changed: 218 additions & 99 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package de.symeda.sormas.ui.caze.importer;
2+
3+
import java.beans.IntrospectionException;
4+
import java.beans.PropertyDescriptor;
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.lang.reflect.InvocationTargetException;
8+
import java.util.function.Consumer;
9+
10+
import org.apache.commons.lang3.StringUtils;
11+
12+
import com.opencsv.exceptions.CsvValidationException;
13+
import com.vaadin.server.StreamResource;
14+
import com.vaadin.ui.UI;
15+
16+
import de.symeda.sormas.api.FacadeProvider;
17+
import de.symeda.sormas.api.i18n.I18nProperties;
18+
import de.symeda.sormas.api.i18n.Validations;
19+
import de.symeda.sormas.api.importexport.InvalidColumnException;
20+
import de.symeda.sormas.api.infrastructure.InfrastructureType;
21+
import de.symeda.sormas.api.region.CountryDto;
22+
import de.symeda.sormas.api.user.UserReferenceDto;
23+
import de.symeda.sormas.api.utils.ValidationRuntimeException;
24+
import de.symeda.sormas.ui.importer.CountryImportProgressLayout;
25+
import de.symeda.sormas.ui.importer.ImportErrorException;
26+
import de.symeda.sormas.ui.importer.ImportLineResult;
27+
import de.symeda.sormas.ui.importer.ImportProgressLayout;
28+
import de.symeda.sormas.ui.importer.InfrastructureImporter;
29+
30+
public class CountryImporter extends InfrastructureImporter {
31+
32+
public CountryImporter(File inputFile, UserReferenceDto currentUser) {
33+
super(inputFile, currentUser, InfrastructureType.COUNTRY);
34+
}
35+
36+
@Override
37+
protected ImportLineResult importDataFromCsvLine(
38+
String[] values,
39+
String[] entityClasses,
40+
String[] entityProperties,
41+
String[][] entityPropertyPaths,
42+
boolean firstLine)
43+
throws IOException, InvalidColumnException {
44+
45+
if (values.length > entityProperties.length) {
46+
writeImportError(values, I18nProperties.getValidationError(Validations.importLineTooLong));
47+
return ImportLineResult.ERROR;
48+
}
49+
50+
CountryDto newEntityDto = CountryDto.build();
51+
boolean iHasImportError = insertRowIntoData(values, entityClasses, entityPropertyPaths, false, (cellData) -> {
52+
try {
53+
if (!StringUtils.isEmpty(cellData.getValue())) {
54+
insertColumnEntryIntoData(newEntityDto, cellData.getValue(), cellData.getEntityPropertyPath());
55+
}
56+
} catch (ImportErrorException | InvalidColumnException e) {
57+
return e;
58+
}
59+
return null;
60+
});
61+
62+
if (!iHasImportError) {
63+
try {
64+
FacadeProvider.getCountryFacade().saveCountry(newEntityDto);
65+
return ImportLineResult.SUCCESS;
66+
} catch (ValidationRuntimeException e) {
67+
writeImportError(values, e.getMessage());
68+
return ImportLineResult.DUPLICATE;
69+
}
70+
} else {
71+
return ImportLineResult.ERROR;
72+
}
73+
}
74+
75+
/**
76+
* Inserts the entry of a single cell into the infrastructure object.
77+
*/
78+
private void insertColumnEntryIntoData(CountryDto newEntityDto, String value, String[] entityPropertyPath)
79+
throws InvalidColumnException, ImportErrorException {
80+
81+
Object currentElement = newEntityDto;
82+
for (int i = 0; i < entityPropertyPath.length; i++) {
83+
String headerPathElementName = entityPropertyPath[i];
84+
85+
try {
86+
if (i != entityPropertyPath.length - 1) {
87+
currentElement = new PropertyDescriptor(headerPathElementName, currentElement.getClass()).getReadMethod().invoke(currentElement);
88+
} else {
89+
PropertyDescriptor pd = new PropertyDescriptor(headerPathElementName, currentElement.getClass());
90+
Class<?> propertyType = pd.getPropertyType();
91+
92+
if (!executeDefaultInvokings(pd, currentElement, value, entityPropertyPath)) {
93+
throw new UnsupportedOperationException(
94+
I18nProperties.getValidationError(Validations.importPropertyTypeNotAllowed, propertyType.getName()));
95+
}
96+
}
97+
} catch (IntrospectionException e) {
98+
throw new InvalidColumnException(buildEntityProperty(entityPropertyPath));
99+
} catch (InvocationTargetException | IllegalAccessException e) {
100+
throw new ImportErrorException(
101+
I18nProperties.getValidationError(Validations.importErrorInColumn, buildEntityProperty(entityPropertyPath)));
102+
} catch (IllegalArgumentException e) {
103+
throw new ImportErrorException(value, buildEntityProperty(entityPropertyPath));
104+
} catch (ImportErrorException e) {
105+
throw e;
106+
} catch (Exception e) {
107+
logger.error("Unexpected error when trying to import infrastructure data: " + e.getMessage());
108+
throw new ImportErrorException(I18nProperties.getValidationError(Validations.importUnexpectedError));
109+
}
110+
}
111+
}
112+
113+
public void startImport(Consumer<StreamResource> errorReportConsumer, UI currentUI) throws IOException, CsvValidationException {
114+
startImport(errorReportConsumer, currentUI, true);
115+
}
116+
117+
@Override
118+
protected ImportProgressLayout getImportProgressLayout(UI currentUI, boolean duplicatesPossible) throws IOException, CsvValidationException {
119+
return new CountryImportProgressLayout(readImportFileLength(inputFile), currentUI, this::cancelImport);
120+
}
121+
}

sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/infrastructure/ImportAllCountriesLayout.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import de.symeda.sormas.api.i18n.I18nProperties;
1616
import de.symeda.sormas.api.i18n.Strings;
1717
import de.symeda.sormas.api.infrastructure.InfrastructureType;
18+
import de.symeda.sormas.ui.caze.importer.CountryImporter;
1819
import de.symeda.sormas.ui.importer.AbstractImportLayout;
1920
import de.symeda.sormas.ui.importer.DataImporter;
2021
import de.symeda.sormas.ui.importer.ImportLayoutComponent;
@@ -31,8 +32,8 @@ public ImportAllCountriesLayout() {
3132
File countriesFile = Paths.get(countriesFileUri).toFile();
3233
resetDownloadErrorReportButton();
3334
try {
34-
DataImporter importer = new InfrastructureImporter(countriesFile, currentUser, InfrastructureType.COUNTRY);
35-
importer.startImport(this::extendDownloadErrorReportButton, currentUI, true);
35+
CountryImporter importer = new CountryImporter(countriesFile, currentUser);
36+
importer.startImport(this::extendDownloadErrorReportButton, currentUI);
3637
} catch (IOException | CsvValidationException e) {
3738
new Notification(
3839
I18nProperties.getString(Strings.headingImportFailed),

sormas-ui/src/main/java/de/symeda/sormas/ui/configuration/infrastructure/InfrastructureImportLayout.java

Lines changed: 45 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package de.symeda.sormas.ui.configuration.infrastructure;
22

3-
import java.io.File;
43
import java.io.IOException;
5-
import java.util.function.Consumer;
64

75
import com.opencsv.exceptions.CsvValidationException;
86
import com.vaadin.server.ClassResource;
97
import com.vaadin.server.Page;
10-
import com.vaadin.server.StreamResource;
118
import com.vaadin.ui.Label;
129
import com.vaadin.ui.Notification;
1310
import com.vaadin.ui.Notification.Type;
@@ -17,6 +14,7 @@
1714
import de.symeda.sormas.api.i18n.I18nProperties;
1815
import de.symeda.sormas.api.i18n.Strings;
1916
import de.symeda.sormas.api.infrastructure.InfrastructureType;
17+
import de.symeda.sormas.ui.caze.importer.CountryImporter;
2018
import de.symeda.sormas.ui.importer.AbstractImportLayout;
2119
import de.symeda.sormas.ui.importer.DataImporter;
2220
import de.symeda.sormas.ui.importer.ImportReceiver;
@@ -36,14 +34,12 @@ public InfrastructureImportLayout(InfrastructureType infrastructureType) {
3634
addComponent(lblCollectionDateInfo);
3735
addComponent(dfCollectionDate);
3836
dfCollectionDate.setRequired(true);
39-
dfCollectionDate.addValueChangeListener(e -> {
40-
upload.setEnabled(e.getProperty().getValue() != null);
41-
});
37+
dfCollectionDate.addValueChangeListener(e -> upload.setEnabled(e.getProperty().getValue() != null));
4238
}
4339

44-
String templateFilePath = null;
45-
String templateFileName = null;
46-
String fileNameAddition = null;
40+
String templateFilePath;
41+
String templateFileName;
42+
String fileNameAddition;
4743
switch (infrastructureType) {
4844
case COMMUNITY:
4945
templateFilePath = FacadeProvider.getImportFacade().getCommunityImportTemplateFilePath();
@@ -94,58 +90,48 @@ public InfrastructureImportLayout(InfrastructureType infrastructureType) {
9490
new ClassResource("/SORMAS_Infrastructure_Import_Guide.pdf"),
9591
new ClassResource("/doc/SORMAS_Data_Dictionary.xlsx"));
9692
addDownloadImportTemplateComponent(2, templateFilePath, templateFileName);
97-
addImportCsvComponent(3, new ImportReceiver(fileNameAddition, new Consumer<File>() {
93+
addImportCsvComponent(3, new ImportReceiver(fileNameAddition, file -> {
94+
resetDownloadErrorReportButton();
9895

99-
@Override
100-
public void accept(File file) {
101-
resetDownloadErrorReportButton();
102-
103-
try {
104-
DataImporter importer;
105-
switch (infrastructureType) {
106-
case COMMUNITY:
107-
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.COMMUNITY);
108-
break;
109-
case DISTRICT:
110-
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.DISTRICT);
111-
break;
112-
case FACILITY:
113-
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.FACILITY);
114-
break;
115-
case POINT_OF_ENTRY:
116-
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.POINT_OF_ENTRY);
117-
break;
118-
case POPULATION_DATA:
119-
importer = new PopulationDataImporter(file, currentUser, dfCollectionDate.getValue());
120-
break;
121-
case COUNTRY:
122-
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.COUNTRY);
123-
break;
124-
case REGION:
125-
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.REGION);
126-
break;
127-
case AREA:
128-
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.AREA);
129-
break;
130-
default:
131-
throw new UnsupportedOperationException(
132-
"Import is currently not implemented for infrastructure type " + infrastructureType.name());
133-
}
134-
135-
importer.startImport(new Consumer<StreamResource>() {
136-
137-
@Override
138-
public void accept(StreamResource resource) {
139-
extendDownloadErrorReportButton(resource);
140-
}
141-
}, currentUI, true);
142-
} catch (IOException | CsvValidationException e) {
143-
new Notification(
144-
I18nProperties.getString(Strings.headingImportFailed),
145-
I18nProperties.getString(Strings.messageImportFailed),
146-
Type.ERROR_MESSAGE,
147-
false).show(Page.getCurrent());
96+
try {
97+
DataImporter importer;
98+
switch (infrastructureType) {
99+
case COMMUNITY:
100+
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.COMMUNITY);
101+
break;
102+
case DISTRICT:
103+
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.DISTRICT);
104+
break;
105+
case FACILITY:
106+
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.FACILITY);
107+
break;
108+
case POINT_OF_ENTRY:
109+
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.POINT_OF_ENTRY);
110+
break;
111+
case POPULATION_DATA:
112+
importer = new PopulationDataImporter(file, currentUser, dfCollectionDate.getValue());
113+
break;
114+
case COUNTRY:
115+
importer = new CountryImporter(file, currentUser);
116+
break;
117+
case REGION:
118+
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.REGION);
119+
break;
120+
case AREA:
121+
importer = new InfrastructureImporter(file, currentUser, InfrastructureType.AREA);
122+
break;
123+
default:
124+
throw new UnsupportedOperationException(
125+
"Import is currently not implemented for infrastructure type " + infrastructureType.name());
148126
}
127+
128+
importer.startImport(this::extendDownloadErrorReportButton, currentUI, true);
129+
} catch (IOException | CsvValidationException e) {
130+
new Notification(
131+
I18nProperties.getString(Strings.headingImportFailed),
132+
I18nProperties.getString(Strings.messageImportFailed),
133+
Type.ERROR_MESSAGE,
134+
false).show(Page.getCurrent());
149135
}
150136
}));
151137

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package de.symeda.sormas.ui.importer;
2+
3+
import com.vaadin.ui.UI;
4+
5+
public class CountryImportProgressLayout extends ImportProgressLayout {
6+
7+
private static final long serialVersionUID = 550057797894417947L;
8+
9+
public CountryImportProgressLayout(int totalCount, UI currentUI, Runnable cancelCallback) {
10+
super(totalCount, currentUI, cancelCallback, true, false);
11+
}
12+
13+
}

sormas-ui/src/main/java/de/symeda/sormas/ui/importer/DataImporter.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public abstract class DataImporter {
7171
/**
7272
* The input CSV file that contains the data to be imported.
7373
*/
74-
private File inputFile;
74+
protected File inputFile;
7575
/**
7676
* Whether or not the import file is supposed to have an additional row on top containing the entity name.
7777
* This is necessary for importers that also import data that is not referenced in the root entity,
@@ -116,10 +116,9 @@ public DataImporter(File inputFile, boolean hasEntityClassRow, UserReferenceDto
116116
public void startImport(Consumer<StreamResource> errorReportConsumer, UI currentUI, boolean duplicatesPossible)
117117
throws IOException, CsvValidationException {
118118

119-
ImportProgressLayout progressLayout =
120-
new ImportProgressLayout(readImportFileLength(inputFile), currentUI, this::cancelImport, duplicatesPossible);
119+
ImportProgressLayout progressLayout = this.getImportProgressLayout(currentUI, duplicatesPossible);
121120

122-
importedLineCallback = result -> progressLayout.updateProgress(result);
121+
importedLineCallback = progressLayout::updateProgress;
123122

124123
Window window = VaadinUiUtil.createPopupWindow();
125124
window.setCaption(I18nProperties.getString(Strings.headingDataImport));
@@ -186,6 +185,13 @@ public void startImport(Consumer<StreamResource> errorReportConsumer, UI current
186185
importThread.start();
187186
}
188187

188+
/**
189+
* Can be overriden by subclasses to provide alternative progress layouts
190+
*/
191+
protected ImportProgressLayout getImportProgressLayout(UI currentUI, boolean duplicatesPossible) throws IOException, CsvValidationException {
192+
return new ImportProgressLayout(readImportFileLength(inputFile), currentUI, this::cancelImport, duplicatesPossible);
193+
}
194+
189195
/**
190196
* To be called by async import thread or unit test
191197
*/
@@ -269,7 +275,7 @@ public void cancelImport() {
269275
}
270276

271277
protected Writer createErrorReportWriter() throws IOException {
272-
File errorReportFile = new File(errorReportFilePath.toString());
278+
File errorReportFile = new File(errorReportFilePath);
273279
if (errorReportFile.exists()) {
274280
errorReportFile.delete();
275281
}

0 commit comments

Comments
 (0)