IRB Exchange

Show / Hide Table of Contents

Sites

Site data originates from participating sites and is uploaded to the IRB Exchange after the participating site downloads the external study.

What's Involved

  1. Adding a method to add a participant to a study in the IRB Exchange from the sIRB side.
  2. Modifying the state transition Invitation Pending -> Awaiting Site Materials to trigger the add participant operation.
  3. Adding a method to create JSON representing a site's data on the pSite side for upload to the IRB Exchange.
  4. Adding a method to parse JSON representing a site's data that is downloaded from the IRB Exchange and set site data.

Add Participant

When the sIRB approves the site invitation, this method will upload metadata about the chosen site Principal Investigator to the IRB Exchange and grant access to the study in the IRB Exchange to the participating site.

_IRBSubmission.getSiteMss Method Example

/** Gets the MSS for this site.
 *
 * @returns (_IRBSubmission)
 **/
function getSiteMss()
{
    return this.getQualifiedAttribute("customAttributes.mSSStudy");
}

_IRBSubmission.onApproveSiteInvite Method Example

/** Handles logic for when a site invitation is approved.
 **/
function onApproveSiteInvite() {
    var isExchangeEnabled = _IRBSettings.getIRBSettings().getQualifiedAttribute("customAttributes.isIRBExchangeEnabled");
    if(isExchangeEnabled == true) {
        var accountName = this.getQualifiedAttribute("customAttributes.IRB.customAttributes.iRBExchangeAccountName");
        if(accountName != null) {
            var pSiteIp = this.getQualifiedAttribute("customAttributes.mSSPSiteInstitutionalProfile");
            var pSiteExchangeID = pSiteIp.getExchangeId();
            if(pSiteExchangeID != null) {
                var exchangeClient = ClickIRBUtils.getExchangeClient(accountName);

                // Validate the PI has email set
                var mss = this.getSiteMss();
                var piEmail = mss.getQualifiedAttribute("customAttributes.investigator.customAttributes.studyTeamMember.contactInformation.emailPreferred.emailAddress");
                if (piEmail == null) {
                    throw new Error("Study PI must have email address set to upload study to IRB Exchange");
                }

                // Validate the site's PI has email set
                var sitePiPerson = this.getQualifiedAttribute("customAttributes.investigator.customAttributes.studyTeamMember");
                var sitePiEmail = sitePiPerson.getQualifiedAttribute("contactInformation.emailPreferred.emailAddress");
                if (sitePiEmail == null) {
                    throw new Error("Site PI must have email address set to upload study to IRB Exchange and give access to the site");
                }

                // Create a JSON object to store the site PI info to be later downloaded by the pSite
                var jsonObj = {};
                jsonObj.firstName = sitePiPerson.getQualifiedAttribute("firstName");
                jsonObj.lastName = sitePiPerson.getQualifiedAttribute("lastName");
                jsonObj.email = sitePiEmail;
                if(exchangeClient.AddParticipant(mss, pSiteExchangeID, JSON.stringify(jsonObj, null, null)) == true) {
                    this.setQualifiedAttribute("customAttributes.canDownloadExchangeUpdates", true);
                    this.setQualifiedAttribute("customAttributes.exchangeInfo.customAttributes.hasPSiteBeenApproved", false);
                }
            }
        }
    }
}

Technical notes about these method examples:

  • The _IRBSubmission.getSiteMss method is used to get the multi-site study for the site.
  • Validation ensures the study and site Principal Investigators have preferred email set for matching on the participating site side.
  • The customAttributes.canDownloadExchangeUpdates attribute is set to true to denote that it can now download updates for the site from the IRB Exchange.
  • The method IrbExchange.AddParticipant is called to perform the operations on the IRB Exchange.

Invitation Pending -> Awaiting Site Materials State Transition

The post-processing script needs to be modified to trigger the add participant operation for the site.

Invitation Pending -> Awaiting Site Materials Post-Processing Script Example

// Upload pSite to the exchange if it's being used
targetEntity.onApproveSiteInvite();

Technical note about this script example:

  • The _IRBSubmission.onApproveSiteInvite method is called to do the work.

Create Site JSON

_IRBSubmission.toSiteJson Method Example

/** Converts the relevant data for a site into JSON for upload to the IRB Exchange.
 *
 * @returns {{JSON}} Metadata representation of the site's data
 */
function toSiteJson() {
    // Set most of site level data
    var jsonObj = {};
    jsonObj.poRef =                 this + "";
    jsonObj.siteId =                this.getQualifiedAttribute("ID");
    jsonObj.status =                this.getQualifiedAttribute("status.ID");
    jsonObj.description =           this.getQualifiedAttribute("customAttributes.siteDescription");

    // Set PI
    var piObj = {};
    piObj.firstName =               this.getQualifiedAttribute("customAttributes.investigator.customAttributes.studyTeamMember.firstName");
    piObj.lastName =                this.getQualifiedAttribute("customAttributes.investigator.customAttributes.studyTeamMember.lastName");
    piObj.email =                   this.getQualifiedAttribute("customAttributes.investigator.customAttributes.studyTeamMember.contactInformation.emailPreferred.emailAddress");
    piObj.hasFinancialInterest =    this.getQualifiedAttribute("customAttributes.investigator.customAttributes.hasFinancialInterest");
    jsonObj.principalInvestigator = piObj;

    // Set primary contact
    var primaryContactObj = {};
    primaryContactObj.firstName =   this.getQualifiedAttribute("customAttributes.primaryContact.customAttributes.studyTeamMember.firstName");
    primaryContactObj.lastName =    this.getQualifiedAttribute("customAttributes.primaryContact.customAttributes.studyTeamMember.lastName");
    primaryContactObj.email =       this.getQualifiedAttribute("customAttributes.primaryContact.customAttributes.studyTeamMember.contactInformation.emailPreferred.emailAddress");
    primaryContactObj.poRef =       this.getQualifiedAttribute("customAttributes.primaryContact.customAttributes.studyTeamMember") + "";
    jsonObj.primaryContact = primaryContactObj;

    // Set PI proxy
    var piProxies = this.getQualifiedAttribute("customAttributes.piProxiesPerson");
    if(piProxies != null) {
        var piProxyObj = {};
        var piProxiesObj = [];
        piProxies = piProxies.elements();
        var piProxiesCount = piProxies.count();
        var piProxy;
        for(var i = 1; i <= piProxiesCount; i++) {
            piProxy = piProxies.item(i);
            piProxyObj.firstName = piProxy.firstName;
            piProxyObj.lastName = piProxy.lastName;
            piProxyObj.email = piProxy.getQualifiedAttribute("contactInformation.emailPreferred.emailAddress");
            piProxiesObj.push(piProxyObj);
            //reset the piProxy object for assigning new values.
            piProxyObj = {};
        }
        jsonObj.piProxies = piProxiesObj;
    }

    // Funding sources
    var jsonDataMap = [];
    jsonDataMap.push({"jsonName":"name",           "attrPath":"customAttributes.organization.name"});
    jsonDataMap.push({"jsonName":"sponsorId",      "attrPath":"customAttributes.fundingSourceID"});
    jsonDataMap.push({"jsonName":"grantsOfficeId", "attrPath":"customAttributes.grantOfficeID"});
    jsonDataMap.push({"jsonName":"documents",      "attrPath":"customAttributes.attachments",      "isDocs":"true"});
    jsonObj.fundingSources = toEsetJson(this, "customAttributes.localFundingSources", jsonDataMap);

    jsonObj.consentForms =         getDocSetPoRefs(this.getQualifiedAttribute("customAttributes.recruitmentDetails.customAttributes.consentForms"));
    jsonObj.recruitmentMaterials = getDocSetPoRefs(this.getQualifiedAttribute("customAttributes.recruitmentDetails.customAttributes.recruitmentAttachments"));
    jsonObj.attachments =          getDocSetPoRefs(this.getQualifiedAttribute("customAttributes.attachments"));

    // Set a flag in the JSON to indicate if pSite has been approved yet or not
    jsonObj.hasPSiteBeenApproved = this.getQualifiedAttribute("customAttributes.exchangeInfo.customAttributes.hasPSiteBeenApproved");

    return JSON.stringify(jsonObj, null, null);
}

Parsing Site JSON

_IRBSubmission.setFromSiteJson Method Example

/** Takes JSON from the IRB Exchange for a site and sets the data onto the appropriate site.
 *
 * @param siteJson (JSON) JSON of the site containing its data
 * @param exchangeClient (IrbExchange) Handle to the etype to run operations on against the Exchange
 * @param activity (Activity) The activity being executed as part of the update from Exchange
 */
function setFromSiteJson(siteJson, exchangeClient, activity) {
    var ipOrg = this.getQualifiedAttribute("customAttributes.mSSPSiteInstitutionalProfile.customAttributes.institution");
    var isSubscribedToProfileData = ClickIntegrationUtils.IsStoreSubscribedToPublication("ProfileData");
    var draft = this.getQualifiedAttribute("customAttributes.draftStudy");
    var docsToDownload = [];
    var mss = this.getSiteMss();

    // If the draft has not yet been created, then directly update SmartForm data
    if(draft == null) {
        // Get the site PI and create the Person for them if needed
        var pi = getMatchedPersonFromExchangeData(siteJson.principalInvestigator.email, siteJson.principalInvestigator.firstName, siteJson.principalInvestigator.lastName, ipOrg, null, isSubscribedToProfileData, this, "customAttributes.investigator.customAttributes.studyTeamMember");
        if(pi != null) {
            setAttributeAndChangeCheck(this, pi, "customAttributes.investigator.customAttributes.studyTeamMember", activity, false);
        }

        // Set the scalar data
        setAttributeAndChangeCheck(this, siteJson.principalInvestigator.hasFinancialInterest, "customAttributes.investigator.customAttributes.hasFinancialInterest", activity, false);
        var description = siteJson.description;
        if(description != null) {
            setAttributeAndChangeCheck(this, description, "customAttributes.siteDescription", activity, false);
        }

        // Set the eSet data
        docsToDownload = updateEsetFromExchange(mss, "customAttributes.localFundingSources", siteJson.fundingSources, "_FundingSource", exchangeClient, docsToDownload, this, activity);

        // Set the site level documents
        docsToDownload = downloadDocSet(exchangeClient, mss, this, "customAttributes.recruitmentDetails.customAttributes.consentForms", siteJson.consentForms, true, docsToDownload, this, "Recruitment Details.Consent Forms", null, null, null, activity);
        docsToDownload = downloadDocSet(exchangeClient, mss, this, "customAttributes.recruitmentDetails.customAttributes.recruitmentAttachments", siteJson.recruitmentMaterials, true, docsToDownload, this, "Recruitment Details.Recruitment Attachments", null, null, null, activity);
        docsToDownload = downloadDocSet(exchangeClient, mss, this, "customAttributes.attachments", siteJson.attachments, true, docsToDownload, this, "Site Supporting Documents", null, null, null, activity);

        // If the pSite has been approved (so its draft exists), then set a flag here and create the draft
        this.setQualifiedAttribute("customAttributes.exchangeInfo.customAttributes.hasPSiteBeenApproved", siteJson.hasPSiteBeenApproved);
        var hasSirbSiteBeenApproved = this.getQualifiedAttribute("customAttributes.exchangeInfo.customAttributes.hasSIRBSiteBeenApproved");
        // If the flag indicating pSite approval is true or null/undefined due to older data from Exchange and sIRB site has been approved, then create draft 
        if(siteJson.hasPSiteBeenApproved != false && hasSirbSiteBeenApproved == true) {
            //if there are no docs create the snapshot now
            //if there are docs we need to wait until all docs are downloaded (see ClickIRBRemoteMethods.addChangeTrackingInfo)
            if (docsToDownload.length == 0){
                this.createSiteSnapshotUsingWorkaround();
            }
            //create Draft
            this.cloneIRBSubmission(activity);
        }
    }

    // Get the site primary contact and create the Person for them if needed
    var primaryContact = getMatchedPersonFromExchangeData(siteJson.primaryContact.email, siteJson.primaryContact.firstName, siteJson.primaryContact.lastName, ipOrg, null, isSubscribedToProfileData, this, "customAttributes.primaryContact.customAttributes.studyTeamMember");
    if(primaryContact != null) {
        setAttributeAndChangeCheck(this, primaryContact, "customAttributes.primaryContact.customAttributes.studyTeamMember", activity, false);
    }

    // Set PI proxies
    setSelectionSetFromJson("Person", this, "customAttributes.piProxiesPerson", siteJson.piProxies, "contactInformation.emailPreferred.emailAddress", activity);

    // Non-SmartForm scalar data
    this.setQualifiedAttribute("customAttributes.cededStudyID", siteJson.siteId);
    setAttributeAndChangeCheck(this, siteJson.status, "customAttributes.externalStudyStatus", activity, false);

    // Set readers and editors
    mss.updateReadersAndEditors(this);

    return docsToDownload;
}
Back to top © 2017 Huron Consulting Group Inc. and affiliates.
Use and distribution prohibited except through written agreement with Huron. Trademarks used in this website are registered or unregistered trademarks of Huron or its licensors.