package org.akaza.openclinica.service.crfdata; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.akaza.openclinica.bean.core.Utils; import org.akaza.openclinica.bean.login.UserAccountBean; import org.akaza.openclinica.bean.managestudy.StudyBean; import org.akaza.openclinica.bean.submit.CRFVersionBean; import org.akaza.openclinica.dao.hibernate.CrfDao; import org.akaza.openclinica.dao.hibernate.CrfVersionDao; import org.akaza.openclinica.dao.hibernate.CrfVersionMediaDao; import org.akaza.openclinica.dao.hibernate.ItemDao; import org.akaza.openclinica.dao.hibernate.ItemDataTypeDao; import org.akaza.openclinica.dao.hibernate.ItemFormMetadataDao; import org.akaza.openclinica.dao.hibernate.ItemGroupDao; import org.akaza.openclinica.dao.hibernate.ItemGroupMetadataDao; import org.akaza.openclinica.dao.hibernate.ItemReferenceTypeDao; import org.akaza.openclinica.dao.hibernate.ResponseTypeDao; import org.akaza.openclinica.dao.hibernate.SectionDao; import org.akaza.openclinica.dao.hibernate.StudyDao; import org.akaza.openclinica.dao.hibernate.UserAccountDao; import org.akaza.openclinica.dao.hibernate.VersioningMapDao; import org.akaza.openclinica.domain.datamap.CrfBean; import org.akaza.openclinica.domain.datamap.CrfVersion; import org.akaza.openclinica.domain.datamap.CrfVersionMedia; import org.akaza.openclinica.domain.datamap.Item; import org.akaza.openclinica.domain.datamap.ItemDataType; import org.akaza.openclinica.domain.datamap.ItemFormMetadata; import org.akaza.openclinica.domain.datamap.ItemGroup; import org.akaza.openclinica.domain.datamap.ItemGroupMetadata; import org.akaza.openclinica.domain.datamap.ResponseSet; import org.akaza.openclinica.domain.datamap.ResponseType; import org.akaza.openclinica.domain.datamap.Section; import org.akaza.openclinica.domain.datamap.VersioningMap; import org.akaza.openclinica.domain.datamap.VersioningMapId; import org.akaza.openclinica.domain.xform.XformContainer; import org.akaza.openclinica.domain.xform.XformGroup; import org.akaza.openclinica.domain.xform.XformItem; import org.akaza.openclinica.domain.xform.XformUtils; import org.akaza.openclinica.domain.xform.dto.Bind; import org.akaza.openclinica.domain.xform.dto.Group; import org.akaza.openclinica.domain.xform.dto.Html; import org.akaza.openclinica.domain.xform.dto.UserControl; import org.akaza.openclinica.validator.xform.ItemValidator; import org.apache.commons.fileupload.FileItem; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.DataBinder; import org.springframework.validation.Errors; @Service public class XformMetaDataService { protected final Logger logger = LoggerFactory.getLogger(getClass().getName()); @Autowired private StudyDao studyDao; @Autowired private CrfDao crfDao; @Autowired private SectionDao sectionDao; @Autowired private UserAccountDao userDao; @Autowired private CrfVersionDao crfVersionDao; @Autowired private CrfVersionMediaDao crfVersionMediaDao; @Autowired private ItemGroupDao itemGroupDao; @Autowired private ItemGroupMetadataDao itemGroupMetadataDao; @Autowired private VersioningMapDao versioningMapDao; @Autowired private ItemFormMetadataDao itemFormMetadataDao; @Autowired private ItemDao itemDao; @Autowired private ItemDataTypeDao itemDataTypeDao; @Autowired private ItemReferenceTypeDao itemRefTypeDao; @Autowired private ResponseTypeDao responseTypeDao; @Autowired private ResponseSetService responseSetService; public Errors runService(CRFVersionBean version, XformContainer container, StudyBean currentStudy, UserAccountBean ub, Html html, String submittedCrfName, String submittedCrfVersionName, String submittedCrfVersionDescription, String submittedRevisionNotes, String submittedXformText, List<FileItem> formItems) { // Create container for holding validation errors DataBinder dataBinder = new DataBinder(new CrfVersion()); Errors errors = dataBinder.getBindingResult(); try { createCRFMetaData(version, container, currentStudy, ub, html, submittedCrfName, submittedCrfVersionName, submittedCrfVersionDescription, submittedRevisionNotes, submittedXformText, formItems, errors); } catch (Exception e) { // Transaction has been rolled back due to an exception. // TODO: Should we add an error message here? logger.error("Error encountered while saving CRF: " + e.getMessage()); logger.error(ExceptionUtils.getStackTrace(e)); } return errors; } @Transactional public CrfVersion createCRFMetaData(CRFVersionBean version, XformContainer container, StudyBean currentStudy, UserAccountBean ub, Html html, String submittedCrfName, String submittedCrfVersionName, String submittedCrfVersionDescription, String submittedRevisionNotes, String submittedXformText, List<FileItem> formItems, Errors errors) throws Exception { CrfVersion crfVersion; // Retrieve CrfBean. Create one if it doesn't exist yet. CrfBean crf = null; if (version.getCrfId() > 0) { crf = (CrfBean) crfDao.findById(version.getCrfId()); crf.setUpdateId(ub.getId()); crf.setDateUpdated(new Date()); crfDao.saveOrUpdate(crf); crfVersion = crfVersionDao.findByOcOID(version.getOid()); // crfVersion.setXform(submittedXformText); crfVersion.setXformName(container.getInstanceName()); crfVersion = crfVersionDao.saveOrUpdate(crfVersion); } else { crf = new CrfBean(); crf.setName(submittedCrfName); crf.setDescription(submittedCrfVersionDescription); crf.setUserAccount(userDao.findById(ub.getId())); crf.setStatus(org.akaza.openclinica.domain.Status.AVAILABLE); crf.setStudy(studyDao.findById(currentStudy.getId())); crf.setOcOid(crfDao.getValidOid(new CrfBean(), submittedCrfName)); crf.setUpdateId(ub.getId()); crf.setDateUpdated(new Date()); Integer crfId = (Integer) crfDao.save(crf); crf.setCrfId(crfId); // Create new CRF Version crfVersion = new CrfVersion(); crfVersion.setName(submittedCrfVersionName); crfVersion.setDescription(submittedCrfVersionDescription); crfVersion.setCrf(crf); crfVersion.setUserAccount(userDao.findById(ub.getId())); crfVersion.setStatus(org.akaza.openclinica.domain.Status.AVAILABLE); crfVersion.setRevisionNotes(submittedRevisionNotes); crfVersion.setOcOid(crfVersionDao.getValidOid(new CrfVersion(), crf.getOcOid(), crfVersion.getName())); crfVersion.setXform(submittedXformText); crfVersion.setXformName(container.getInstanceName()); Integer crfVersionId = (Integer) crfVersionDao.save(crfVersion); crfVersion.setCrfVersionId(crfVersionId); } // Create Section Section section = sectionDao.findByCrfVersionOrdinal(crfVersion.getCrfVersionId(), 1); if (section == null) { section = new Section(); section.setCrfVersion(crfVersion); section.setStatus(org.akaza.openclinica.domain.Status.AVAILABLE); section.setLabel(""); section.setTitle(""); section.setSubtitle(""); section.setPageNumberLabel(""); section.setOrdinal(1); section.setUserAccount(userDao.findById(ub.getId())); // not null section.setBorders(0); sectionDao.saveOrUpdate(section); section = sectionDao.findByCrfVersionOrdinal(crfVersion.getCrfVersionId(), 1); } createGroups(container, html, submittedXformText, crf, crfVersion, section, ub, errors); saveMedia(formItems, crf, crfVersion); if (errors.hasErrors()) { logger.error("Encounter validation errors while saving CRF. Rolling back transaction."); throw new RuntimeException("Encountered validation errors while saving CRF."); } return crfVersion; } private void saveMedia(List<FileItem> items, CrfBean crf, CrfVersion version) { boolean hasFiles = false; for (FileItem item : items) { if (!item.isFormField() && item.getName() != null && !item.getName().isEmpty()) hasFiles = true; } if (hasFiles) { String dir = Utils.getCrfMediaFilePath(crf, version); // Save any media files for (FileItem item : items) { if (!item.isFormField()) { String fileName = item.getName(); // Some browsers IE 6,7 getName returns the whole path int startIndex = fileName.lastIndexOf('\\'); if (startIndex != -1) { fileName = fileName.substring(startIndex + 1, fileName.length()); } CrfVersionMedia media = crfVersionMediaDao.findByCrfVersionIdAndFileName(version.getCrfVersionId(), fileName); if (media == null) { media = new CrfVersionMedia(); media.setCrfVersion(version); media.setName(fileName); media.setPath(dir); crfVersionMediaDao.saveOrUpdate(media); } } } } } private void createGroups(XformContainer container, Html html, String submittedXformText, CrfBean crf, CrfVersion version, Section section, UserAccountBean ub, Errors errors) throws Exception { Integer itemOrdinal = 1; ArrayList<String> usedGroupOids = new ArrayList<String>(); ArrayList<String> usedItemOids = new ArrayList<String>(); List<Group> htmlGroups = html.getBody().getGroup(); for (Group htmlGroup : htmlGroups) { XformGroup xformGroup = container.findGroupByRef(htmlGroup.getRef()); ItemGroup itemGroup = itemGroupDao.findByNameCrfId(xformGroup.getGroupName(), crf); if (itemGroup == null) { itemGroup = new ItemGroup(); itemGroup.setName(xformGroup.getGroupName()); itemGroup.setCrf(crf); itemGroup.setStatus(org.akaza.openclinica.domain.Status.AVAILABLE); itemGroup.setUserAccount(userDao.findById(ub.getId())); itemGroup.setOcOid(itemGroupDao.getValidOid(new ItemGroup(), crf.getName(), xformGroup.getGroupName(), usedGroupOids)); usedGroupOids.add(itemGroup.getOcOid()); Integer itemGroupId = (Integer) itemGroupDao.save(itemGroup); itemGroup.setItemGroupId(itemGroupId); } List<UserControl> widgets = null; boolean isRepeating = false; if (htmlGroup.getRepeat() != null && htmlGroup.getRepeat().getUsercontrol() != null) { widgets = htmlGroup.getRepeat().getUsercontrol(); isRepeating = true; } else { widgets = htmlGroup.getUsercontrol(); } // Create Item specific DB entries: item, response_set,item_form_metadata,versioning_map,item_group_metadata for (UserControl widget : widgets) { // Skip reserved name and read-only items here XformItem xformItem = container.findItemByGroupAndRef(xformGroup, widget.getRef()); String readonly = html.getHead().getModel().getBindByNodeSet(widget.getRef()).getReadOnly(); if (!xformItem.getItemName().equals("OC.STUDY_SUBJECT_ID") && !xformItem.getItemName().equals("OC.STUDY_SUBJECT_ID_CONFIRM") && (readonly == null || !readonly.trim().equals("true()"))) { Item item = createItem(html, widget, xformGroup, xformItem, crf, ub, usedItemOids, errors); if (item != null) { ResponseType responseType = getResponseType(html, xformItem); ResponseSet responseSet = responseSetService.getResponseSet(html, submittedXformText, xformItem, version, responseType, item, errors); // add if statement ItemFormMetadata ifmd = itemFormMetadataDao.findByItemCrfVersion(item.getItemId(), version.getCrfVersionId()); if (ifmd == null) { ifmd = createItemFormMetadata(html, xformItem, item, responseSet, section, version, itemOrdinal); } createVersioningMap(version, item); // ItemGroupMetadata igmd = itemGroupMetadataDao.findByItemCrfVersion(item.getItemId(), version.getCrfVersionId()); if (igmd == null) { igmd = createItemGroupMetadata(html, item, version, itemGroup, isRepeating, itemOrdinal); } itemOrdinal++; } } } } } private ItemGroupMetadata createItemGroupMetadata(Html html, Item item, CrfVersion version, ItemGroup itemGroup, boolean isRepeating, Integer itemOrdinal) { ItemGroupMetadata itemGroupMetadata = new ItemGroupMetadata(); itemGroupMetadata.setItemGroup(itemGroup); itemGroupMetadata.setHeader(""); itemGroupMetadata.setSubheader(""); itemGroupMetadata.setLayout(""); if (isRepeating) { itemGroupMetadata.setRepeatingGroup(true); itemGroupMetadata.setRepeatNumber(1); itemGroupMetadata.setRepeatMax(40); } else { itemGroupMetadata.setRepeatingGroup(false); itemGroupMetadata.setRepeatNumber(1); itemGroupMetadata.setRepeatMax(1); } itemGroupMetadata.setRepeatArray(""); itemGroupMetadata.setRowStartNumber(0); itemGroupMetadata.setCrfVersion(version); itemGroupMetadata.setItem(item); itemGroupMetadata.setOrdinal(itemOrdinal); itemGroupMetadata.setShowGroup(true); itemGroupMetadata = itemGroupMetadataDao.saveOrUpdate(itemGroupMetadata); return itemGroupMetadata; } private void createVersioningMap(CrfVersion version, Item item) { VersioningMapId versioningMapId = new VersioningMapId(); versioningMapId.setCrfVersionId(version.getCrfVersionId()); versioningMapId.setItemId(item.getItemId()); VersioningMap versioningMap = new VersioningMap(); versioningMap.setCrfVersion(version); versioningMap.setItem(item); versioningMap.setVersionMapId(versioningMapId); versioningMapDao.saveOrUpdate(versioningMap); } private ItemFormMetadata createItemFormMetadata(Html html, XformItem xformItem, Item item, ResponseSet responseSet, Section section, CrfVersion version, Integer itemOrdinal) { ItemFormMetadata itemFormMetadata = new ItemFormMetadata(); itemFormMetadata.setCrfVersionId(version.getCrfVersionId()); itemFormMetadata.setResponseSet(responseSet); itemFormMetadata.setItem(item); itemFormMetadata.setSubheader(""); itemFormMetadata.setHeader(""); itemFormMetadata.setLeftItemText(getLeftItemText(html, xformItem)); itemFormMetadata.setRightItemText(""); itemFormMetadata.setParentId(0); itemFormMetadata.setSection(section); itemFormMetadata.setOrdinal(itemOrdinal); itemFormMetadata.setParentLabel(""); itemFormMetadata.setColumnNumber(0); itemFormMetadata.setPageNumberLabel(""); itemFormMetadata.setQuestionNumberLabel(""); itemFormMetadata.setRegexp(""); itemFormMetadata.setRegexpErrorMsg(""); if (getItemFormMetadataRequired(html, xformItem)) itemFormMetadata.setRequired(true); else itemFormMetadata.setRequired(false); itemFormMetadata.setDefaultValue(""); itemFormMetadata.setResponseLayout("Vertical"); itemFormMetadata.setWidthDecimal(""); itemFormMetadata.setShowItem(true); itemFormMetadata = itemFormMetadataDao.saveOrUpdate(itemFormMetadata); return itemFormMetadata; } private Item createItem(Html html, UserControl widget, XformGroup xformGroup, XformItem xformItem, CrfBean crf, UserAccountBean ub, ArrayList<String> usedItemOids, Errors errors) throws Exception { ItemDataType newDataType = getItemDataType(html, xformItem); if (newDataType == null) { logger.error("Found unsupported item type for item: " + xformItem.getItemName()); return null; } Item item = itemDao.findByNameCrfId(xformItem.getItemName(), crf.getCrfId()); ItemDataType oldDataType = null; if (item != null) { oldDataType = itemDataTypeDao.findByItemDataTypeId(itemDao.getItemDataTypeId(item)); } else { item = new Item(); item.setName(xformItem.getItemName()); item.setDescription(""); item.setUnits(""); item.setPhiStatus(false); item.setItemDataType(newDataType); item.setItemReferenceType(itemRefTypeDao.findByItemReferenceTypeId(1)); item.setStatus(org.akaza.openclinica.domain.Status.AVAILABLE); item.setUserAccount(userDao.findById(ub.getId())); item.setOcOid(itemDao.getValidOid(new Item(), crf.getName(), xformItem.getItemName(), usedItemOids)); usedItemOids.add(item.getOcOid()); Integer itemId = (Integer) itemDao.save(item); item.setItemId(itemId); } ItemValidator validator = new ItemValidator(itemDao, oldDataType, newDataType); DataBinder dataBinder = new DataBinder(item); Errors itemErrors = dataBinder.getBindingResult(); validator.validate(item, itemErrors); errors.addAllErrors(itemErrors); return itemDao.findByOcOID(item.getOcOid()); } private String getLeftItemText(Html html, XformItem xformItem) { for (Group group : html.getBody().getGroup()) { if (group.getRepeat() != null && group.getRepeat().getUsercontrol() != null) { for (UserControl control : group.getRepeat().getUsercontrol()) { if (control.getRef().equals(xformItem.getItemPath())) { if (control.getLabel() != null && control.getLabel().getLabel() != null) return control.getLabel().getLabel(); else if (control.getLabel() != null && control.getLabel().getRef() != null && !control.getLabel().getRef().equals("")) { String ref = control.getLabel().getRef(); String itextKey = ref.substring(ref.indexOf("'") + 1, ref.lastIndexOf("'")); return XformUtils.getDefaultTranslation(html, itextKey); } else return ""; } } } else { for (UserControl control : group.getUsercontrol()) { if (control.getRef().equals(xformItem.getItemPath())) { if (control.getLabel() != null && control.getLabel().getLabel() != null) return control.getLabel().getLabel(); else if (control.getLabel() != null && control.getLabel().getRef() != null && !control.getLabel().getRef().equals("")) { String ref = control.getLabel().getRef(); String itextKey = ref.substring(ref.indexOf("'") + 1, ref.lastIndexOf("'")); return XformUtils.getDefaultTranslation(html, itextKey); } else return ""; } } } } return ""; } private ItemDataType getItemDataType(Html html, XformItem xformItem) { String dataType = ""; for (Bind bind : html.getHead().getModel().getBind()) { if (bind.getNodeSet().equals(xformItem.getItemPath()) && bind.getType() != null && !bind.getType().equals("")) { dataType = bind.getType(); if (dataType.equals("string")) return itemDataTypeDao.findByItemDataTypeCode("ST"); else if (dataType.equals("int")) return itemDataTypeDao.findByItemDataTypeCode("INT"); else if (dataType.equals("decimal")) return itemDataTypeDao.findByItemDataTypeCode("REAL"); else if (dataType.equals("select") || dataType.equals("select1")) return itemDataTypeDao.findByItemDataTypeCode("ST"); else if (dataType.equals("binary")) return itemDataTypeDao.findByItemDataTypeCode("FILE"); else if (dataType.equals("date")) return itemDataTypeDao.findByItemDataTypeCode("DATE"); } } return null; } private boolean getItemFormMetadataRequired(Html html, XformItem xformItem) { boolean required = false; for (Bind bind : html.getHead().getModel().getBind()) { if (bind.getNodeSet().equals(xformItem.getItemPath()) && bind.getRequired() != null && !bind.getRequired().equals("")) { if (bind.getRequired().equals("true()")) required = true; else if (bind.getRequired().equals("false()")) required = false; } } return required; } private ResponseType getResponseType(Html html, XformItem xformItem) { String responseType = ""; for (Bind bind : html.getHead().getModel().getBind()) { if (bind.getNodeSet().equals(xformItem.getItemPath()) && bind.getType() != null && !bind.getType().equals("")) { responseType = bind.getType(); if (responseType.equals("string")) return responseTypeDao.findByResponseTypeName("text"); else if (responseType.equals("int")) return responseTypeDao.findByResponseTypeName("text"); else if (responseType.equals("decimal")) return responseTypeDao.findByResponseTypeName("text"); else if (responseType.equals("date")) return responseTypeDao.findByResponseTypeName("text"); else if (responseType.equals("select")) return responseTypeDao.findByResponseTypeName("checkbox"); else if (responseType.equals("select1")) return responseTypeDao.findByResponseTypeName("radio"); else if (responseType.equals("binary")) return responseTypeDao.findByResponseTypeName("file"); else return null; } } return null; } }