diff --git a/avni-server-api/src/main/java/org/avni/server/importer/batch/csv/writer/LocationWriter.java b/avni-server-api/src/main/java/org/avni/server/importer/batch/csv/writer/LocationWriter.java index e3b20a385..440f0957d 100644 --- a/avni-server-api/src/main/java/org/avni/server/importer/batch/csv/writer/LocationWriter.java +++ b/avni-server-api/src/main/java/org/avni/server/importer/batch/csv/writer/LocationWriter.java @@ -177,11 +177,15 @@ public void setLocationUploadMode(String locationUploadMode) { this.locationUploadMode = locationUploadMode; } - private enum LocationUploadMode { + public enum LocationUploadMode { relaxed, strict; public static boolean isRelaxedMode(String mode) { return mode == null || LocationUploadMode.valueOf(mode).equals(relaxed); } + + public static boolean isRelaxedMode(LocationUploadMode mode) { + return mode.equals(relaxed); + } } } diff --git a/avni-server-api/src/main/java/org/avni/server/service/ImportLocationsConstants.java b/avni-server-api/src/main/java/org/avni/server/service/ImportLocationsConstants.java new file mode 100644 index 000000000..d33d4e80a --- /dev/null +++ b/avni-server-api/src/main/java/org/avni/server/service/ImportLocationsConstants.java @@ -0,0 +1,26 @@ +package org.avni.server.service; + +import org.avni.server.importer.batch.csv.writer.header.LocationHeaders; + +import java.util.Collections; + +public interface ImportLocationsConstants { + String STRING_CONSTANT_NEW_LINE = "\n"; + String STRING_CONSTANT_EMPTY_STRING = ""; + String STRING_PLACEHOLDER_BLOCK = "\"%s\","; + String STRING_3_PLACEHOLDER_BLOCK = String.join("", Collections.nCopies(3, STRING_PLACEHOLDER_BLOCK)); + String Example = "Ex: "; + String ALLOWED_VALUES = "Allowed values: "; + String COLUMN_NAME_GPS_COORDINATES = LocationHeaders.gpsCoordinates; + String COLUMN_NAME_LOCATION_WITH_FULL_HIERARCHY = "Location with full hierarchy"; + String COLUMN_NAME_NEW_LOCATION_NAME = "New location name"; + String COLUMN_NAME_PARENT_LOCATION_WITH_FULL_HIERARCHY = "Parent location with full hierarchy"; + String LOCATION_WITH_FULL_HIERARCHY_DESCRIPTION = "Can be found from Admin -> Locations -> Click Export. Used to specify which location's fields need to be updated. mandatory field"; + String NEW_LOCATION_NAME_DESCRIPTION = "Enter new name here ONLY if it needs to be updated"; + String PARENT_LOCATION_WITH_FULL_HIERARCHY_DESCRIPTION = "Hierarchy of parent location that should contain the child location"; + String LOCATION_WITH_FULL_HIERARCHY_EXAMPLE = "PHC B, Sub B, Vil B"; + String NEW_LOCATION_NAME_EXAMPLE = "Vil C"; + String PARENT_LOCATION_WITH_FULL_HIERARCHY_EXAMPLE = "PHC C, Sub C"; + String GPS_COORDINATES_EXAMPLE = "Ex: 23.45,43.85"; + String ENTER_YOUR_DATA_STARTING_HERE = "Enter your data starting here"; +} diff --git a/avni-server-api/src/main/java/org/avni/server/service/ImportService.java b/avni-server-api/src/main/java/org/avni/server/service/ImportService.java index c16e1fd9e..15d31ce80 100644 --- a/avni-server-api/src/main/java/org/avni/server/service/ImportService.java +++ b/avni-server-api/src/main/java/org/avni/server/service/ImportService.java @@ -7,12 +7,14 @@ import org.avni.server.domain.*; import org.avni.server.domain.Locale; import org.avni.server.framework.security.UserContextHolder; +import org.avni.server.importer.batch.csv.writer.LocationWriter; import org.avni.server.importer.batch.csv.writer.ProgramEnrolmentWriter; import org.avni.server.importer.batch.csv.writer.header.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import java.io.*; import java.util.*; @@ -20,11 +22,8 @@ import java.util.stream.Stream; @Service -public class ImportService { +public class ImportService implements ImportLocationsConstants{ - public static final String GPS_COORDINATES = ",\""+LocationHeaders.gpsCoordinates+"\","; - public static final String GPS_COORDINATES_EXAMPLE = ",\"Ex: 23.45,43.85\","; - public static final String STRING_CONSTANT_NEW_LINE = "\n"; private final SubjectTypeRepository subjectTypeRepository; private final FormMappingRepository formMappingRepository; private static final Logger logger = LoggerFactory.getLogger(ProgramEnrolmentWriter.class); @@ -134,10 +133,6 @@ public String getSampleFile(String uploadType) { String[] uploadSpec = uploadType.split("---"); String response = ""; - if (uploadType.equals("locations")) { - return getLocationsSampleFile(); - } - if (uploadType.equals("usersAndCatchments")) { return getUsersAndCatchmentsSampleFile(); } @@ -169,22 +164,32 @@ public String getSampleFile(String uploadType) { throw new UnsupportedOperationException(String.format("Sample file format for %s not supported", uploadType)); } - private String getLocationsSampleFile() { + public String getLocationsSampleFile(LocationWriter.LocationUploadMode locationUploadMode, String locationHierarchy) { StringBuilder sampleFileBuilder = new StringBuilder(); + List addressLevelTypes = null; try { - List addressLevelTypes = getAddressLevelTypesForCreateModeSingleHierarchy(); + if (LocationWriter.LocationUploadMode.isRelaxedMode(locationUploadMode)) { + addressLevelTypes = getAddressLevelTypesForCreateModeSingleHierarchy(locationHierarchy); + } List formElementNamesForLocationTypeFormElements = getFormElementNamesForLocationTypeForms(); - appendHeaderRowForLocations(sampleFileBuilder, addressLevelTypes, formElementNamesForLocationTypeFormElements); - appendDescriptionForLocations(sampleFileBuilder, addressLevelTypes, formElementNamesForLocationTypeFormElements); + appendHeaderRowForLocations(sampleFileBuilder, locationUploadMode, addressLevelTypes, formElementNamesForLocationTypeFormElements); + appendDescriptionForLocations(sampleFileBuilder, locationUploadMode, addressLevelTypes, formElementNamesForLocationTypeFormElements); + appendExamplesForLocations(sampleFileBuilder, locationUploadMode); } catch (Exception e) { throw new RuntimeException(e); } return sampleFileBuilder.toString(); } - private List getAddressLevelTypesForCreateModeSingleHierarchy() { + private List getAddressLevelTypesForCreateModeSingleHierarchy(String locationHierarchy) throws Exception { + if(!StringUtils.hasText(locationHierarchy)) { + throw new Exception(String.format("Invalid value specified for locationHierarchy: %s", locationHierarchy)); + } + List selectedLocationHierarchy = Arrays.stream(locationHierarchy.split("\\.")) + .map(Long::parseLong).collect(Collectors.toList()); return addressLevelTypeRepository.findAllByIsVoidedFalse().stream() .sorted(Comparator.comparingDouble(AddressLevelType::getLevel).reversed()) + .filter(alt -> selectedLocationHierarchy.contains(alt.getId())) .collect(Collectors.toList()); } @@ -215,23 +220,42 @@ private List createDecisionFormElement(Set concepts) { }).collect(Collectors.toList()); } - private void appendHeaderRowForLocations(StringBuilder sampleFileBuilder, List addressLevelTypes, + private void appendHeaderRowForLocations(StringBuilder sampleFileBuilder, LocationWriter.LocationUploadMode locationUploadMode, List addressLevelTypes, List formElementNamesForLocationTypeFormElements) { - sampleFileBuilder.append(addressLevelTypes.stream() - .map(alt -> String.format("\"%s\"", alt.getName())).collect(Collectors.joining(","))); - sampleFileBuilder.append(GPS_COORDINATES); + if (LocationWriter.LocationUploadMode.isRelaxedMode(locationUploadMode)) { + sampleFileBuilder.append(addressLevelTypes.stream() + .map(alt -> String.format(STRING_PLACEHOLDER_BLOCK, alt.getName())).collect(Collectors.joining(STRING_CONSTANT_EMPTY_STRING))); + } else { + sampleFileBuilder.append(String.format(STRING_3_PLACEHOLDER_BLOCK, COLUMN_NAME_LOCATION_WITH_FULL_HIERARCHY, + COLUMN_NAME_NEW_LOCATION_NAME, COLUMN_NAME_PARENT_LOCATION_WITH_FULL_HIERARCHY)); + } + sampleFileBuilder.append(String.format(STRING_PLACEHOLDER_BLOCK, COLUMN_NAME_GPS_COORDINATES)); sampleFileBuilder.append(formElementNamesForLocationTypeFormElements.stream() - .map(fe -> String.format("\"%s\"", fe.getName())).collect(Collectors.joining(","))); + .map(fe -> String.format(STRING_PLACEHOLDER_BLOCK, fe.getName())).collect(Collectors.joining(STRING_CONSTANT_EMPTY_STRING))); } - private void appendDescriptionForLocations(StringBuilder sampleFileBuilder, List addressLevelTypes, + private void appendDescriptionForLocations(StringBuilder sampleFileBuilder, LocationWriter.LocationUploadMode locationUploadMode, List addressLevelTypes, List formElementNamesForLocationTypeFormElements) { - sampleFileBuilder.append(STRING_CONSTANT_NEW_LINE).append(addressLevelTypes.stream() - .map(alt -> String.format("\"Ex: %s1\"", alt.getName())).collect(Collectors.joining(","))); - sampleFileBuilder.append(GPS_COORDINATES_EXAMPLE); + if (LocationWriter.LocationUploadMode.isRelaxedMode(locationUploadMode)) { + sampleFileBuilder.append(STRING_CONSTANT_NEW_LINE).append(addressLevelTypes.stream() + .map(alt -> String.format(STRING_PLACEHOLDER_BLOCK, Example +alt.getName())).collect(Collectors.joining(STRING_CONSTANT_EMPTY_STRING))); + } else { + sampleFileBuilder.append(STRING_CONSTANT_NEW_LINE).append(String.format(STRING_3_PLACEHOLDER_BLOCK, LOCATION_WITH_FULL_HIERARCHY_DESCRIPTION, + NEW_LOCATION_NAME_DESCRIPTION, PARENT_LOCATION_WITH_FULL_HIERARCHY_DESCRIPTION)); + } + sampleFileBuilder.append(String.format(STRING_PLACEHOLDER_BLOCK, GPS_COORDINATES_EXAMPLE)); sampleFileBuilder.append(formElementNamesForLocationTypeFormElements.stream() - .map(fe -> String.format("\"Allowed values: %s\"", conceptService.getSampleValuesForSyncConcept(fe.getConcept()))) - .collect(Collectors.joining(","))); + .map(fe -> String.format(STRING_PLACEHOLDER_BLOCK, ALLOWED_VALUES +conceptService.getSampleValuesForSyncConcept(fe.getConcept()))) + .collect(Collectors.joining(STRING_CONSTANT_EMPTY_STRING))); + } + + private void appendExamplesForLocations(StringBuilder sampleFileBuilder, LocationWriter.LocationUploadMode locationUploadMode) { + if (LocationWriter.LocationUploadMode.isRelaxedMode(locationUploadMode)) { + sampleFileBuilder.append(STRING_CONSTANT_NEW_LINE).append(ENTER_YOUR_DATA_STARTING_HERE); + } else { + sampleFileBuilder.append(STRING_CONSTANT_NEW_LINE).append(String.format(STRING_3_PLACEHOLDER_BLOCK, LOCATION_WITH_FULL_HIERARCHY_EXAMPLE, + NEW_LOCATION_NAME_EXAMPLE, PARENT_LOCATION_WITH_FULL_HIERARCHY_EXAMPLE)); + } } private String getUsersAndCatchmentsSampleFile() { diff --git a/avni-server-api/src/main/java/org/avni/server/web/ImportController.java b/avni-server-api/src/main/java/org/avni/server/web/ImportController.java index 333e22b1f..8a2f7da50 100644 --- a/avni-server-api/src/main/java/org/avni/server/web/ImportController.java +++ b/avni-server-api/src/main/java/org/avni/server/web/ImportController.java @@ -10,8 +10,10 @@ import org.avni.server.domain.accessControl.PrivilegeType; import org.avni.server.framework.security.UserContextHolder; import org.avni.server.importer.batch.JobService; +import org.avni.server.importer.batch.csv.writer.LocationWriter; import org.avni.server.service.*; import org.avni.server.service.accessControl.AccessControlService; +import org.avni.server.util.BadRequestError; import org.avni.server.web.util.ErrorBodyBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +29,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -75,11 +78,24 @@ public ImportController(JobService jobService, } @RequestMapping(value = "/web/importSample", method = RequestMethod.GET) - public void getSampleImportFile(@RequestParam String uploadType, HttpServletResponse response) throws IOException { + public void getSampleImportFile(@RequestParam String uploadType, + @RequestParam(value = "locationHierarchy", required = false) String locationHierarchy, + @RequestParam(value = "locationUploadMode", required = false) LocationWriter.LocationUploadMode locationUploadMode, + HttpServletResponse response) throws IOException { response.setContentType("text/csv"); response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + uploadType + ".csv\""); - response.getWriter().write(importService.getSampleFile(uploadType)); + if (uploadType.equals("locations")) { + if(!StringUtils.hasText(locationHierarchy)) { + throw new BadRequestError("Invalid value specified for request param \"locationHierarchy\": "+ locationHierarchy); + } + if(locationUploadMode == null) { + throw new BadRequestError("Missing value for request param \"locationUploadMode\""); + } + response.getWriter().write(importService.getLocationsSampleFile(locationUploadMode, locationHierarchy)); + } else { + response.getWriter().write(importService.getSampleFile(uploadType)); + } } @RequestMapping(value = "/web/importTypes", method = RequestMethod.GET)