package gov.samhsa.consent2share.web.controller; import gov.samhsa.consent2share.common.AuthenticatedUser; import gov.samhsa.consent2share.common.UserContext; import gov.samhsa.consent2share.domain.valueset.ValueSet; import gov.samhsa.consent2share.service.dto.CodeSystemDto; import gov.samhsa.consent2share.service.dto.ConceptCodeDto; import gov.samhsa.consent2share.service.dto.ConceptCodeVSCSDto; import gov.samhsa.consent2share.service.dto.ValueSetDto; import gov.samhsa.consent2share.service.valueset.CodeSystemNotFoundException; import gov.samhsa.consent2share.service.valueset.CodeSystemService; import gov.samhsa.consent2share.service.valueset.CodeSystemVersionNotFoundException; import gov.samhsa.consent2share.service.valueset.ConceptCodeNotFoundException; import gov.samhsa.consent2share.service.valueset.ConceptCodeService; import gov.samhsa.consent2share.service.valueset.DuplicateConceptCodeException; import gov.samhsa.consent2share.service.valueset.InvalidCSVException; import gov.samhsa.consent2share.service.valueset.ValueSetNotFoundException; import gov.samhsa.consent2share.service.valueset.ValueSetService; import gov.samhsa.consent2share.web.AjaxException; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.SessionAttributes; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import com.google.common.base.Joiner; @Controller @SessionAttributes("conceptCode") @RequestMapping("/sysadmin") public class ConceptCodeController extends AbstractNodeController { private static final Logger LOGGER = LoggerFactory .getLogger(ConceptCodeController.class); protected static final String ERROR_MESSAGE_KEY_DELETED_CONCEPTCODE_WAS_NOT_FOUND = "Deleted conceptCode was not found."; protected static final String ERROR_MESSAGE_KEY_EDITED_CONCEPTCODE_WAS_NOT_FOUND = "Edited conceptCode was not found."; protected static final String ERROR_MESSAGE_VALUSETS_NOT_FOUND = "Value Sets not Found. To create and manage concept codes, you need to add Value Sets."; protected static final String ERROR_MESSAGE_CODESYSTEMVERSIONS_NOT_FOUND = "Code System Versions not Found. To create and manage concept codes, you need to add Code System Versions."; protected static final String ERROR_MESSAGE_CODESYSTEMS_NOT_FOUND = "Code System Versions not Found. To create and manage concept codes, you need to add Code System Versions."; protected static final String ERROR_MESSAGE_KEY_SELECTED_CODESYSTEM_WAS_NOT_FOUND = "Selected code system was not found."; protected static final String ERROR_MESSAGE_KEY_DUPLICATE_CODE_FOUND = "ConceptCode Already Exists"; protected static final String ERROR_MESSAGE_KEY_SELECTED_VALUESET_WAS_NOT_FOUND = "Selected valueSet was not found."; protected static final String MODEL_ATTIRUTE_CONCEPTCODEDTO = "conceptCodeDto"; protected static final String MODEL_ATTRIBUTE_CONCEPTCODEDTOS = "conceptCodeDtos"; protected static final String CONCEPTCODE_ADD_FORM_VIEW = "views/sysadmin/conceptCodeAdd"; protected static final String CONCEPTCODE_EDIT_FORM_VIEW = "views/sysadmin/conceptCodeEdit"; protected static final String CONCEPTCODE_LIST_VIEW = "views/sysadmin/conceptCodeList"; protected static final String REQUEST_MAPPING_LIST = "/conceptCodeList"; protected static final String REDIRECT_MAPPING_LIST = "../conceptCodeList"; protected static final String REDIRECT_ID_MAPPING_LIST = "../../conceptCodeList"; protected static final String MODEL_ATTIRUTE_CONCEPTCODEVSCSDTO = "conceptCodeVSCSDto"; protected static final String MODEL_ATTIRUTE_CODESYSTEMS = "codeSystems"; protected static final String MODEL_ATTIRUTE_VALSETNAMES = "valueSets"; @Resource private ConceptCodeService conceptCodeService; @Resource private CodeSystemService codeSystemService; @Resource private ValueSetService valueSetService; /** The user context. */ @Autowired UserContext userContext; /** * Processes create conceptCode requests. * * @param model * @return The name of the create conceptCode form view. */ @RequestMapping(value = REQUEST_MAPPING_LIST, method = RequestMethod.GET) public String showCreateConceptCodeForm( Model model, @RequestParam(value = "panelState", required = false, defaultValue = "") String panelState) { ConceptCodeVSCSDto conceptCodeVSCSDto = null; List<CodeSystemDto> codeSystems = null; List<ValueSetDto> valueSets = null; LOGGER.debug("Rendering Concept Code list page"); try { conceptCodeVSCSDto = conceptCodeService.create(); codeSystems = codeSystemService.findAll(); valueSets = valueSetService.findAllWithoutDeletable(); } catch (ValueSetNotFoundException e) { LOGGER.debug("No value sets found in the system"); } catch (CodeSystemNotFoundException e) { LOGGER.debug("No code systems found in the system"); } catch (CodeSystemVersionNotFoundException e) { LOGGER.debug("No code system versions found in the system"); } if (panelState.equals("resetoptions")) { model.addAttribute("panelState", "resetoptions"); } else if (panelState.equals("addnew")) { model.addAttribute("panelState", "addnew"); } model.addAttribute(MODEL_ATTIRUTE_CONCEPTCODEVSCSDTO, conceptCodeVSCSDto); model.addAttribute(MODEL_ATTIRUTE_CODESYSTEMS, codeSystems); model.addAttribute(MODEL_ATTIRUTE_VALSETNAMES, valueSets); return CONCEPTCODE_LIST_VIEW; } /** * Processes the submissions of create conceptCode form. * * @param created * The information of the created conceptCodes. * @param bindingResult * @param attributes * @return */ @RequestMapping(value = "conceptCodeAdd.html", method = RequestMethod.GET) public String conceptCodeAddForm(Model model, HttpServletRequest request) { LOGGER.debug("Rendering Concept Code Add Form "); ConceptCodeVSCSDto conceptCodeVSCSDto = new ConceptCodeVSCSDto(); ConceptCodeDto conceptCodeDto = new ConceptCodeDto(); String returnPage = CONCEPTCODE_ADD_FORM_VIEW; try { conceptCodeVSCSDto = conceptCodeService.create(); conceptCodeVSCSDto.setError(false); conceptCodeDto = (ConceptCodeDto) model.asMap().get( MODEL_ATTIRUTE_CONCEPTCODEDTO); if (conceptCodeDto != null) conceptCodeVSCSDto.setConceptCodeDto(conceptCodeDto); } catch (ValueSetNotFoundException e) { LOGGER.debug("No value sets found in the system"); conceptCodeDto.setError(true); conceptCodeDto.setErrorMessage(ERROR_MESSAGE_VALUSETS_NOT_FOUND); returnPage = CONCEPTCODE_LIST_VIEW; } catch (CodeSystemNotFoundException e) { LOGGER.debug("No code systems found in the system"); conceptCodeDto.setError(true); conceptCodeDto .setErrorMessage(ERROR_MESSAGE_CODESYSTEMVERSIONS_NOT_FOUND); returnPage = CONCEPTCODE_LIST_VIEW; } catch (CodeSystemVersionNotFoundException e) { LOGGER.debug("No code system versions found in the system"); conceptCodeDto.setError(true); conceptCodeDto.setErrorMessage(ERROR_MESSAGE_CODESYSTEMS_NOT_FOUND); returnPage = CONCEPTCODE_LIST_VIEW; } model.addAttribute(MODEL_ATTIRUTE_CONCEPTCODEVSCSDTO, conceptCodeVSCSDto); model.addAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, conceptCodeDto); return returnPage; } /* * Processes the submissions of create conceptCode form. * * @param created The information of the created conceptCodes. * * @param bindingResult * * @param attributes * * @return */ @RequestMapping(value = "/conceptCode/create", method = RequestMethod.POST) public String ceateConceptCodeForm( @Valid @ModelAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO) ConceptCodeDto created, RedirectAttributes redirectAttribute, Model model) { LOGGER.debug("Create conceptCode form was submitted with information: " + created); AuthenticatedUser currentUser = userContext.getCurrentUser(); String path = "../conceptCodeAdd.html"; try { created.setUserName(currentUser.getUsername()); created = conceptCodeService.create(created); created.setError(false); created.setSuccessMessage("Concept Code with code:" + created.getCode() + " and Name: " + created.getName() + " is Added Successfully"); path = REDIRECT_MAPPING_LIST; } catch (DataIntegrityViolationException ex) { LOGGER.info(ex.getLocalizedMessage()); Throwable t = ex.getCause(); String message = null; if (t != null) { message = "Cause: " + t.getMessage(); } created.setError(true); created.setErrorMessage("Concept Code is not Added " + message); } catch (ValueSetNotFoundException e) { LOGGER.debug("No value set found with id: " + created.getValueSetId()); created.setError(true); created.setErrorMessage(ERROR_MESSAGE_KEY_SELECTED_VALUESET_WAS_NOT_FOUND); } catch (CodeSystemNotFoundException e) { LOGGER.debug("No code systm found with id: " + created.getCodeSystemId()); created.setError(true); created.setErrorMessage(ERROR_MESSAGE_KEY_SELECTED_CODESYSTEM_WAS_NOT_FOUND); } catch (DuplicateConceptCodeException e) { LOGGER.debug("Duplicate Code : " + created.getCodeSystemId()); created.setError(true); created.setErrorMessage(ERROR_MESSAGE_KEY_DUPLICATE_CODE_FOUND); } model.addAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, created); redirectAttribute.addFlashAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, created); String panelState = "?panelState=addnew"; return createRedirectViewPath(path) + panelState; } /** * Processes delete conceptCode requests. * * @param id * The id of the deleted conceptCode. * @param attributes * @return */ @RequestMapping(value = "/conceptCode/delete/{id}", method = RequestMethod.DELETE) public @ResponseBody String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttribute) { LOGGER.debug("Deleting conceptCode with id: " + id); ConceptCodeDto deleted = new ConceptCodeDto(); try { deleted = conceptCodeService.delete(id); deleted.setError(false); deleted.setSuccessMessage("ConceptCode with Code: " + deleted.getCode() + " and Name: " + deleted.getName() + " is deleted Successfully."); } catch (ConceptCodeNotFoundException e) { LOGGER.debug("No conceptCode found with id: " + id); deleted.setError(true); deleted.setErrorMessage(ERROR_MESSAGE_KEY_DELETED_CONCEPTCODE_WAS_NOT_FOUND); } redirectAttribute.addFlashAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, deleted); if (deleted.isError == false) { return deleted.getSuccessMessage(); } else { throw new AjaxException(HttpStatus.INTERNAL_SERVER_ERROR, deleted.getErrorMessage()); } } /** * Processes edit conceptCode requests. * * @param id * The id of the edited conceptCode. * @param model * @param attributes * @return The name of the edit conceptCode form view. */ @RequestMapping(value = "/conceptCode/edit/{id}", method = RequestMethod.GET) public String showEditConceptCodeForm(@PathVariable("id") Long id, Model model, RedirectAttributes attributes) { LOGGER.debug("Rendering edit conceptCode form for conceptCode with id: " + id); ConceptCodeDto conceptCodeDto = new ConceptCodeDto(); try { conceptCodeDto = conceptCodeService.findById(id); if (conceptCodeDto == null) { LOGGER.debug("No conceptCode found with id: " + id); conceptCodeDto = new ConceptCodeDto(); conceptCodeDto.setError(true); conceptCodeDto .setErrorMessage(ERROR_MESSAGE_KEY_EDITED_CONCEPTCODE_WAS_NOT_FOUND); return createRedirectViewPath(REDIRECT_ID_MAPPING_LIST); } } catch (ValueSetNotFoundException e) { LOGGER.debug(e.getMessage()); conceptCodeDto.setError(true); conceptCodeDto.setErrorMessage(e.getMessage()); } catch (ConceptCodeNotFoundException e) { LOGGER.debug(e.getMessage()); conceptCodeDto.setError(true); conceptCodeDto.setErrorMessage(e.getMessage()); } model.addAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, conceptCodeDto); return CONCEPTCODE_EDIT_FORM_VIEW; } /** * Processes the submissions of edit conceptCode form. * * @param updated * The information of the edited conceptCode. * @param bindingResult * @param attributes * @return */ @RequestMapping(value = "/conceptCode/edit/{id}", method = RequestMethod.POST) public String submitEditConceptCodeForm( @ModelAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO) ConceptCodeDto updated, @PathVariable("id") Long id, RedirectAttributes redirectAttribute, Model model) { LOGGER.debug("Edit conceptCode form was submitted with information: " + updated + id); String path = REDIRECT_ID_MAPPING_LIST; // "../../conceptCodeEdit.html"; try { AuthenticatedUser currentUser = userContext.getCurrentUser(); updated.setUserName(currentUser.getUsername()); updated.setId(id); updated = conceptCodeService.update(updated); updated.setError(false); updated.setSuccessMessage("Concept Code with Code: " + updated.getCode() + " and Name: " + updated.getName() + " is Edited Successfully"); // path = REDIRECT_ID_MAPPING_LIST; } catch (ConceptCodeNotFoundException e) { LOGGER.debug("No conceptCode was found with id: " + updated.getId()); updated.setError(true); updated.setErrorMessage("Edited Concept Code is not found"); } catch (ValueSetNotFoundException e) { LOGGER.debug(e.getMessage()); updated.setError(true); updated.setErrorMessage(e.getMessage()); } model.addAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, updated); redirectAttribute.addFlashAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, updated); return createRedirectViewPath(path); } /** * This setter method should only be used by unit tests * * @param conceptCodeService */ protected void setConceptCodeService(ConceptCodeService conceptCodeService) { this.conceptCodeService = conceptCodeService; } /** * Search for concept code via AJAX call * * @param searchCategory * @param searchTerm * @return conceptCodes */ @RequestMapping("/conceptCode/ajaxSearchConceptCode/pageNumber/{pageNumber}/searchCategory/{searchCategory}/searchTerm/{searchTerm}/codeSystem/{codeSystem}/codeSystemVersion/{codeSystemVersion}/valueSetName/{valueSetName}") @ResponseStatus(HttpStatus.OK) public @ResponseBody Map<String, Object> searchConceptCode( @PathVariable("searchCategory") String searchCategory, @PathVariable("searchTerm") String searchTerm, @PathVariable("codeSystem") String codeSystem, @PathVariable("codeSystemVersion") String codeSystemVersion, @PathVariable("valueSetName") String valueSetName, @PathVariable("pageNumber") String pageNumber) { codeSystem = validateEmptyFilterParams(codeSystem); codeSystemVersion = validateEmptyFilterParams(codeSystemVersion); valueSetName = validateEmptyFilterParams(valueSetName); searchTerm = validateEmptyFilterParams(searchTerm); Map<String, Object> conceptCodes = null; try { if (searchCategory.equals("code")) conceptCodes = conceptCodeService.findAllByCode(searchTerm, codeSystem, codeSystemVersion, valueSetName, Integer.parseInt(pageNumber)); else if (searchCategory.equals("name")) conceptCodes = conceptCodeService.findAllByName(searchTerm, codeSystem, codeSystemVersion, valueSetName, Integer.parseInt(pageNumber)); } catch (IllegalArgumentException e) { conceptCodes = null; throw new AjaxException( HttpStatus.BAD_REQUEST, "Unable to perform search because the request parameters contained invalid data."); } return conceptCodes; } /** * Populate value set names. * * @param codeSystem * the code system * @param codeSystemVersion * the code system version * @return the map */ @RequestMapping("/conceptCode/populateValueSetNames/codeSystem/{codeSystem}/codeSystemVersion/{codeSystemVersion}") @ResponseStatus(HttpStatus.OK) public @ResponseBody Map<String, String> populateValueSetNames( @PathVariable("codeSystem") String codeSystem, @PathVariable("codeSystemVersion") String codeSystemVersion) { Map<String, String> valueSetMap = new HashMap<String, String>(); for (ValueSet vs : conceptCodeService.findValueSetsByCodeSystem( codeSystem, codeSystemVersion)) valueSetMap.put(vs.getId().toString(), vs.getCode()); return valueSetMap; } /** * Validate empty filter params. * * @param param * the param * @return the string */ private String validateEmptyFilterParams(String param) { return param.replace("empty", ""); } /** * Get all concept codes via AJAX * * @return conceptCodes */ @RequestMapping("/conceptCode/ajaxGetAllConceptCodes") @ResponseStatus(HttpStatus.OK) public @ResponseBody List<ConceptCodeDto> getAllConceptCodes() { List<ConceptCodeDto> conceptCodes = null; conceptCodes = conceptCodeService.findAll(); return conceptCodes; } @RequestMapping("/conceptCode/ajaxGetPagedConceptCodes/pageNumber/{pageNumber}") @ResponseStatus(HttpStatus.OK) public @ResponseBody Map<String, Object> getAllConceptCodes( @PathVariable("pageNumber") String pageNumber) { Map<String, Object> conceptCodesMap = null; conceptCodesMap = conceptCodeService.findAll(Integer .parseInt(pageNumber)); return conceptCodesMap; } @RequestMapping(value = "/conceptCode/batchUpload", method = RequestMethod.POST) public String batchUpload( @RequestParam(value = "codeSystemId", required = false) String codeSystemId, @RequestParam(value = "codeSystemVersionId", required = false) Long codeSystemVersionId, HttpServletRequest request, @RequestParam("batch_file") MultipartFile file, @RequestParam(value = "valueSetIds", required = false) List<Long> valueSetIds, RedirectAttributes redirectAttribute) throws Exception { AuthenticatedUser currentUser = userContext.getCurrentUser(); ConceptCodeDto conceptCodeDto = new ConceptCodeDto(); conceptCodeDto.setUserName(currentUser.getUsername()); try { conceptCodeDto = conceptCodeService.conceptCodeBatchUpload( conceptCodeDto, file, codeSystemId, codeSystemVersionId, valueSetIds); if (conceptCodeDto.getListOfDuplicatesCodes() != null && !conceptCodeDto.getListOfDuplicatesCodes().isEmpty()) { if (conceptCodeDto.getConceptCodesInserted() > 0) { throw new InvalidCSVException( "Added " + conceptCodeDto.getConceptCodesInserted() + " codes with errors. Following rows had duplicates codes: " + Joiner.on(",") .join(conceptCodeDto .getListOfDuplicatesCodes())); } else { throw new InvalidCSVException( "Error uploading concept codes. Following rows had duplicate codes: " + Joiner.on(",") .join(conceptCodeDto .getListOfDuplicatesCodes())); } } else { conceptCodeDto.setSuccessMessage("Added " + conceptCodeDto.getConceptCodesInserted() + " Codes"); } } catch (ValueSetNotFoundException ex) { LOGGER.debug("Invalid value set category found while doing batch upload: " + ex.getMessage()); conceptCodeDto.setError(true); } catch (InvalidCSVException ex) { LOGGER.debug("Invalid csv format found while doing batch upload: " + ex.getMessage()); conceptCodeDto.setError(true); conceptCodeDto.setErrorMessage(ex.getMessage()); } catch (DataIntegrityViolationException ex) { LOGGER.debug("Duplicate value set while doing batch upload: " + ex.getMessage()); conceptCodeDto.setError(true); } redirectAttribute.addFlashAttribute(MODEL_ATTIRUTE_CONCEPTCODEDTO, conceptCodeDto); return createRedirectViewPath(REDIRECT_MAPPING_LIST); } }