package org.akaza.openclinica.controller; import java.io.StringWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.TimeZone; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import org.akaza.openclinica.bean.admin.CRFBean; import org.akaza.openclinica.bean.core.Status; import org.akaza.openclinica.bean.managestudy.EventDefinitionCRFBean; import org.akaza.openclinica.bean.managestudy.StudyBean; import org.akaza.openclinica.bean.managestudy.StudyEventBean; import org.akaza.openclinica.bean.managestudy.StudyEventDefinitionBean; import org.akaza.openclinica.bean.managestudy.StudySubjectBean; import org.akaza.openclinica.bean.service.StudyParameterValueBean; import org.akaza.openclinica.bean.submit.CRFVersionBean; import org.akaza.openclinica.bean.submit.EventCRFBean; import org.akaza.openclinica.dao.admin.CRFDAO; import org.akaza.openclinica.dao.core.CoreResources; import org.akaza.openclinica.dao.hibernate.EventCrfDao; import org.akaza.openclinica.dao.managestudy.StudyDAO; import org.akaza.openclinica.dao.managestudy.StudyEventDefinitionDAO; import org.akaza.openclinica.dao.managestudy.StudySubjectDAO; import org.akaza.openclinica.dao.service.StudyParameterValueDAO; import org.akaza.openclinica.dao.submit.CRFVersionDAO; import org.akaza.openclinica.dao.submit.EventCRFDAO; import org.akaza.openclinica.dao.submit.ItemDataDAO; import org.akaza.openclinica.domain.datamap.EventCrf; import org.akaza.openclinica.i18n.util.ResourceBundleProvider; import org.akaza.openclinica.service.ParticipantEventService; import org.akaza.openclinica.service.pmanage.ParticipantPortalRegistrar; import org.akaza.openclinica.web.pform.PFormCache; import org.apache.commons.dbcp.BasicDataSource; import org.apache.commons.lang.exception.ExceptionUtils; import org.cdisc.ns.odm.v130_api.ODM; import org.cdisc.ns.odm.v130_api.ODMcomplexTypeDefinitionClinicalData; import org.cdisc.ns.odm.v130_api.ODMcomplexTypeDefinitionFormData; import org.cdisc.ns.odm.v130_api.ODMcomplexTypeDefinitionStudyEventData; import org.cdisc.ns.odm.v130_api.ODMcomplexTypeDefinitionSubjectData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; 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.ResponseBody; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping(value = "/odmk") public class OdmController { @Autowired @Qualifier("dataSource") private BasicDataSource dataSource; @Autowired ServletContext context; @Autowired RuleController ruleController; @Autowired EventCrfDao eventCrfDao; public static final String FORM_CONTEXT = "ecid"; ParticipantPortalRegistrar participantPortalRegistrar; StudyDAO sdao; protected final Logger logger = LoggerFactory.getLogger(getClass().getName()); /** * @api {get} /pages/odmk/studies/:studyOid/metadata Retrieve metadata * @apiName getStudyMetadata * @apiPermission admin * @apiVersion 3.8.0 * @apiParam {String} studyOid Study Oid. * @apiGroup Study * @apiDescription Retrieve the metadata of the specified study * @apiParamExample {json} Request-Example: * { * "studyOid": "S_BL101", * } * @apiSuccessExample {json} Success-Response: * HTTP/1.1 200 OK * { * The whole Study Metadata * } */ @RequestMapping(value = "/studies/{study}/metadata", method = RequestMethod.GET) public ModelAndView getStudyMetadata(Model model, HttpSession session, @PathVariable("study") String studyOid, HttpServletResponse response) throws Exception { if (!mayProceed(studyOid)) return null; return ruleController.studyMetadata(model, session, studyOid, response); } /** * This URL needs to change ... Right now security disabled on this ... You can call this with * http://localhost:8080/OpenClinica-web-MAINLINE-SNAPSHOT /pages/odmk/studies/S_DEFAULTS1/events * * @param studyOid * @return * @throws Exception */ /** * @api {get} /pages/odmk/study/:studyOid/studysubject/:studySubjectOid/events Retrieve an event - participant * @apiName getEvent * @apiPermission Module participate - enabled * @apiVersion 3.8.0 * @apiParam {String} studyOid Study Oid. * @apiParam {String} studySubjectOid Study Subject Oid * @apiGroup Study Event * @apiDescription Retrieve an event with earliest start date and ordinal. * @apiParamExample {json} Request-Example: * { * "studyOid": "S_BL101", * "studySubjectOid": "SS_DYN101" * } * @apiSuccessExample {json} Success-Response: * HTTP/1.1 200 OK * { * "id": null, * "signature": [], * "clinicalData": [{ * "annotations": [], * "subjectData": [{ * "annotation": [], * "signature": null, * "status": "available", * "dateOfBirth": null, * "uniqueIdentifier": null, * "studyEventData": [{ * "annotation": [], * "signature": null, * "status": null, * "eventName": "Scoring Visit", * "studyEventRepeatKey": null, * "endDate": null, * "formData": [{ * "annotation": [], * "signature": null, * "status": "Not Started", * "interviewerName": null, * "formOID": "F_SCORING2_CRF_V10", * "itemGroupData": [], * "url": * "http://localhost:8006/::YYYF?iframe=true&ecid=a480dc4479409f6fe99a03d472f5cf77f4f12fb2b5ac471b9d35d737d934b042" * , * "version": null, * "transactionType": null, * "auditRecord": null, * "archiveLayoutRef": null, * "formDataElementExtension": [], * "formRepeatKey": null, * "interviewDate": null, * "formName": "Scoring2_CRF", * "versionDescription": "Scoring2", * "statusChangeTimeStamp": null * }], * "studyEventOID": null, * "transactionType": null, * "auditRecord": null, * "studyEventDataElementExtension": [], * "studyEventLocation": null, * "startDate": "2015-08-27 12:00:00.0", * "subjectAgeAtEvent": null * }], * "studySubjectID": "DYN101", * "transactionType": null, * "yearOfBirth": null, * "auditRecord": null, * "investigatorRef": null, * "siteRef": null, * "subjectDataElementExtension": [], * "subjectKey": "SS_DYN101", * "secondaryID": null, * "sex": null * }], * "studyName": "Baseline Study 101", * "studyOID": "S_BL101", * "metaDataVersionOID": null, * "auditRecords": [], * "signatures": [] * }], * "fileType": null, * "fileOID": null, * "description": null, * "study": [], * "association": [], * "odmversion": null, * "creationDateTime": null, * "adminData": [], * "referenceData": [], * "granularity": null, * "archival": null, * "priorFileOID": null, * "asOfDateTime": null, * "originator": null, * "sourceSystem": null, * "sourceSystemVersion": null * } */ @RequestMapping(value = "/study/{studyOid}/studysubject/{studySubjectOid}/events", method = RequestMethod.GET) public @ResponseBody ODM getEvent(@PathVariable("studyOid") String studyOid, @PathVariable("studySubjectOid") String studySubjectOid) throws Exception { ResourceBundleProvider.updateLocale(new Locale("en_US")); return getODM(studyOid, studySubjectOid); } private ODM getODM(String studyOID, String subjectKey) { ODM odm = new ODM(); String ssoid = subjectKey; if (ssoid == null) { return null; } CRFVersionDAO versionDAO = new CRFVersionDAO(dataSource); StudyDAO studyDAO = new StudyDAO(dataSource); StudySubjectDAO studySubjectDAO = new StudySubjectDAO(dataSource); EventCRFDAO eventCRFDAO = new EventCRFDAO(dataSource); ItemDataDAO itemDataDAO = new ItemDataDAO(dataSource); CRFDAO crfDAO = new CRFDAO(dataSource); List<ODMcomplexTypeDefinitionFormData> formDatas = new ArrayList<>(); try { // Retrieve crfs for next event StudySubjectBean studySubjectBean = studySubjectDAO.findByOid(ssoid); ParticipantEventService participantEventService = new ParticipantEventService(dataSource); StudyEventBean nextEvent = participantEventService.getNextParticipantEvent(studySubjectBean); if (nextEvent != null) { logger.debug("Found event: " + nextEvent.getName() + " - ID: " + nextEvent.getId()); List<EventCRFBean> eventCrfs = eventCRFDAO.findAllByStudyEvent(nextEvent); StudyBean study = studyDAO.findByOid(studyOID); if (!mayProceed(studyOID, studySubjectBean)) return odm; List<EventDefinitionCRFBean> eventDefCrfs = participantEventService.getEventDefCrfsForStudyEvent(studySubjectBean, nextEvent); for (EventDefinitionCRFBean eventDefCrf:eventDefCrfs) { if (eventDefCrf.isParticipantForm()) { EventCRFBean eventCRF = participantEventService.getExistingEventCRF(studySubjectBean, nextEvent, eventDefCrf); boolean itemDataExists = false; boolean validStatus = true; CRFVersionBean crfVersion = null; if (eventCRF!=null) { if (eventCRF.getStatus().getId() != 1 && eventCRF.getStatus().getId() != 2) validStatus = false; if (itemDataDAO.findAllByEventCRFId(eventCRF.getId()).size() > 0) itemDataExists = true; crfVersion = (CRFVersionBean) versionDAO.findByPK(eventCRF.getCRFVersionId()); } else crfVersion = (CRFVersionBean) versionDAO.findByPK(eventDefCrf.getDefaultVersionId()); if (validStatus) { String formUrl = null; if (!itemDataExists) formUrl = createEnketoUrl(studyOID, crfVersion, nextEvent, ssoid); else formUrl = createEditUrl(studyOID, crfVersion, nextEvent, ssoid); formDatas.add(getFormDataPerCrf(crfVersion, nextEvent, eventCrfs, crfDAO, formUrl, itemDataExists)); } } } return createOdm(study, studySubjectBean, nextEvent, formDatas); } else { logger.debug("Unable to find next event for subject."); } } catch (Exception e) { logger.error(e.getMessage()); logger.error(ExceptionUtils.getStackTrace(e)); } return odm; } private StudyEventDefinitionBean getStudyEventDefinitionBean(int ID) { StudyEventDefinitionDAO seddao = new StudyEventDefinitionDAO(dataSource); StudyEventDefinitionBean studyEventDefinitionBean = (StudyEventDefinitionBean) seddao.findByPK(ID); return studyEventDefinitionBean; } private ODM createOdm(StudyBean study, StudySubjectBean studySubjectBean, StudyEventBean nextEvent, List<ODMcomplexTypeDefinitionFormData> formDatas) { ODM odm = new ODM(); ODMcomplexTypeDefinitionClinicalData clinicalData = generateClinicalData(study); ODMcomplexTypeDefinitionSubjectData subjectData = generateSubjectData(studySubjectBean); ODMcomplexTypeDefinitionStudyEventData studyEventData = generateStudyEventData(nextEvent); // Create the object graph studyEventData.getFormData().addAll(formDatas); subjectData.getStudyEventData().add(studyEventData); clinicalData.getSubjectData().add(subjectData); odm.getClinicalData().add(clinicalData); return odm; } private String createEnketoUrl(String studyOID, CRFVersionBean crfVersion, StudyEventBean nextEvent, String ssoid) throws Exception { PFormCache cache = PFormCache.getInstance(context); String enketoURL = cache.getPFormURL(studyOID, crfVersion.getOid()); String contextHash = cache.putSubjectContext(ssoid, String.valueOf(nextEvent.getStudyEventDefinitionId()), String.valueOf(nextEvent.getSampleOrdinal()), crfVersion.getOid()); String url = enketoURL + "?" + FORM_CONTEXT + "=" + contextHash; logger.debug("Enketo URL for " + crfVersion.getName() + "= " + url); return url; } private String createEditUrl(String studyOID, CRFVersionBean crfVersion, StudyEventBean nextEvent, String ssoid) throws Exception { PFormCache cache = PFormCache.getInstance(context); String contextHash = cache.putSubjectContext(ssoid, String.valueOf(nextEvent.getStudyEventDefinitionId()), String.valueOf(nextEvent.getSampleOrdinal()), crfVersion.getOid()); String editURL = CoreResources.getField("sysURL.base") + "pages/api/v1/editform/" + studyOID + "/url"; String url = editURL + "?" + FORM_CONTEXT + "=" + contextHash; logger.debug("Edit URL for " + crfVersion.getName() + "= " + url); return url; } private ODMcomplexTypeDefinitionFormData getFormDataPerCrf(CRFVersionBean crfVersion, StudyEventBean nextEvent, List<EventCRFBean> eventCrfs, CRFDAO crfDAO, String formUrl,boolean itemDataExists) { EventCRFBean selectedEventCRFBean = null; CRFBean crfBean = (CRFBean) crfDAO.findByVersionId(crfVersion.getId()); for (EventCRFBean eventCRFBean : eventCrfs) { if (eventCRFBean.getCRFVersionId() == crfVersion.getId()) { selectedEventCRFBean = eventCRFBean; break; } } return generateFormData(crfVersion, nextEvent, selectedEventCRFBean, crfBean, formUrl, itemDataExists); } private ODMcomplexTypeDefinitionClinicalData generateClinicalData(StudyBean study) { ODMcomplexTypeDefinitionClinicalData clinicalData = new ODMcomplexTypeDefinitionClinicalData(); clinicalData.setStudyName(study.getName()); clinicalData.setStudyOID(study.getOid()); return clinicalData; } private ODMcomplexTypeDefinitionSubjectData generateSubjectData(StudySubjectBean studySubject) { ODMcomplexTypeDefinitionSubjectData subjectData = new ODMcomplexTypeDefinitionSubjectData(); subjectData.setSubjectKey(studySubject.getOid()); subjectData.setStudySubjectID(studySubject.getLabel()); subjectData.setStatus(studySubject.getStatus().getName()); return subjectData; } private ODMcomplexTypeDefinitionStudyEventData generateStudyEventData(StudyEventBean studyEvent) { ODMcomplexTypeDefinitionStudyEventData studyEventData = new ODMcomplexTypeDefinitionStudyEventData(); studyEventData.setStartDate(studyEvent.getDateStarted().toString()); StudyEventDefinitionBean studyEventDefBean = getStudyEventDefinitionBean(studyEvent.getStudyEventDefinitionId()); studyEventData.setEventName(studyEventDefBean.getName()); studyEventData.setStudyEventOID(studyEventDefBean.getOid()); studyEventData.setStudyEventRepeatKey(String.valueOf(studyEvent.getSampleOrdinal())); return studyEventData; } private ODMcomplexTypeDefinitionFormData generateFormData(CRFVersionBean crfVersionBean, StudyEventBean nextEvent, EventCRFBean eventCRFBean, CRFBean crfBean, String formUrl,boolean itemDataExists) { ODMcomplexTypeDefinitionFormData formData = new ODMcomplexTypeDefinitionFormData(); formData.setFormOID(crfVersionBean.getOid()); formData.setFormName(crfBean.getName()); formData.setVersionDescription(crfVersionBean.getDescription()); formData.setUrl(formUrl); if (eventCRFBean == null) { formData.setStatus("Not Started"); } else { EventCrf eventCrf = eventCrfDao.findById(eventCRFBean.getId()); if (!itemDataExists){ formData.setStatus("Not Started"); }else{ formData.setStatus(eventCRFBean.getStatus().getName()); } if (eventCrf.getDateUpdated() != null) { // returns time as UTC SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); formData.setStatusChangeTimeStamp(sdf.format(eventCrf.getDateUpdated())); } } return formData; } /** * Currently not used, but keep here for future unit test * * @param clazz * @param odm * @return * @throws Exception */ private String generateXmlFromObj(Class clazz, ODM odm) throws Exception { JAXBContext context = JAXBContext.newInstance(clazz); Marshaller m = context.createMarshaller(); StringWriter w = new StringWriter(); m.marshal(odm, w); return w.toString(); } @SuppressWarnings("unchecked") private void sortList(ArrayList<EventDefinitionCRFBean> edcBeans) { Collections.sort(edcBeans, new Comparator() { public int compare(Object o1, Object o2) { Integer x1 = ((EventDefinitionCRFBean) o1).getOrdinal(); Integer x2 = ((EventDefinitionCRFBean) o2).getOrdinal(); int sComp = x1.compareTo(x2); return sComp; } }); } private StudyBean getStudy(String oid) { sdao = new StudyDAO(dataSource); StudyBean studyBean = (StudyBean) sdao.findByOid(oid); return studyBean; } private StudyBean getParentStudy(String studyOid) { StudyBean study = getStudy(studyOid); if (study.getParentStudyId() == 0) { return study; } else { StudyBean parentStudy = (StudyBean) sdao.findByPK(study.getParentStudyId()); return parentStudy; } } private boolean mayProceed(String studyOid, StudySubjectBean ssBean) throws Exception { boolean accessPermission = false; logger.info(" studySubjectStatus: " + ssBean.getStatus().getName()); System.out.println(" studySubjectStatus: " + ssBean.getStatus().getName()); if (mayProceed(studyOid) && ssBean.getStatus() == Status.AVAILABLE) { accessPermission = true; } return accessPermission; } private boolean mayProceed(String studyOid) throws Exception { boolean accessPermission = false; StudyBean study = getParentStudy(studyOid); StudyParameterValueDAO spvdao = new StudyParameterValueDAO(dataSource); StudyParameterValueBean pStatus = spvdao.findByHandleAndStudy(study.getId(), "participantPortal"); participantPortalRegistrar = new ParticipantPortalRegistrar(); String pManageStatus = participantPortalRegistrar.getRegistrationStatus(studyOid).toString(); // ACTIVE , // PENDING , // INACTIVE String participateStatus = pStatus.getValue().toString(); // enabled , disabled String studyStatus = study.getStatus().getName().toString(); // available , pending , frozen , locked System.out.println("pManageStatus: " + pManageStatus + " participantStatus: " + participateStatus + " studyStatus: " + studyStatus); logger.info("pManageStatus: " + pManageStatus + " participantStatus: " + participateStatus + " studyStatus: " + studyStatus); if (participateStatus.equalsIgnoreCase("enabled") && studyStatus.equalsIgnoreCase("available") && pManageStatus.equalsIgnoreCase("ACTIVE")) { accessPermission = true; } return accessPermission; } }