IRB Exchange

Show / Hide Table of Contents

Download List

In Huron IRB, we implemented a list control on the IRB page under the IRB Exchange tab to facilitate downloads of studies and reportable new information submissions. This list will show all submissions available to download for all IRB Exchange accounts setup on IRB offices.

What's Involved

  1. Adding a Site Designer page to implement the user interface controls and client side logic for downloading submissions.
  2. Adding a Site Designer page to handle server requests from the download list controls and server side logic.

Implementing the Client Side Controls

Create a Site Designer page at Applications:\IRB\Exchange\DownloadList.

Create the following controls in the following order:

  • Push Button
    • Set onclick event to searchItemsToDownload(true);
  • Group
    • Set id to downloadList
    • Set renderMode to div
  • Client Side Script (See example below)
  • Server Side Script (see example below)
  • Group
    • Set id to dialogDownload
    • Set renderMode to div

Client Side Script Example

window.onload = function() {
    searchItemsToDownload(true);
};

function populateTable(data){
    var rows = "";
    var items = data.items;
    var resultCount = items.length;
    // Clear out the existing table values
    $("#downloadList").empty();

    if(resultCount > 0) {
        var html = "<table class='DataTable' border='0' cellspacing='0' cellpadding='3'>";
        html += "<thead><td class='DisplayHead' width='null'>Submission Type</td><td class='DisplayHead' width='null'>Submission Name</td>";
        html += "<td class='DisplayHead' width='null'>Institution</td>";
        html += "<td class='DisplayHead' width='null'>Lead Principal Investigator</td>";
        html += "<td class='DisplayHead' width='null'>pSite Principal Investigator</td></thead><tbody>";

        var row;
        var tr = "<tr class='drsv-row'>";
        var td = "<td class='List' selectedClass='ListSelected' align='left' style='vertical-align: center;'>";
        var button = "<input class='Button2' type='button' value='Download' onclick=\"";
        $.each(items, function (i, item) {
            if (item.submissionType == "reportable-new-information") {
                row = tr + td + "RNI</td>";
            } else {
                row = tr + td + "Study</td>";
            }
            row += td + item.name + "</td>";
            row += td + item.owner.name + "</td>";
            row += td + item.principalInvestigator + "</td>";
            row += td + item.sitePrincipalInvestigator + "</td>";
            if (item.submissionType == "reportable-new-information") {
                row += td + button + "downloadRni('" + item.containerId + "','" + item.exchangeRef + "','" + item.accountName + "')\"></td></tr>";
            } else if(item.clientFunctionName == "downloadStudy") {
                row += td + button + item.clientFunctionName + "('" + item.exchangeRef + "','" + item.adminOffices[0] + "')\"></td></tr>";
            } else {
                row += td + button + item.clientFunctionName + "('" + item.exchangeRef + "','" + item.adminOffices + "')\"></td></tr>";
            }
            rows += row;
        });
        html += rows;
        html += "</tbody></table>";
    } else {
        html = "<p style='text-align: center'>There are no items available to download</p>";
    }

    $("#downloadList").append(html);
}

function searchItemsToDownload(showDialog)
{
    // Display a dialog informing the user of progress
    if(showDialog == true) {
        jQuery(function () {
            jQuery("#dialogDownload").dialog({
                buttons: {
                },
                title: "Getting list of items to download..."
            });
            jQuery("#dialogDownload").html("Getting list of items to download. This will take a few moments...");
        });
    }

    var searchValue = jQuery("#searchBox").val();
    if(searchValue == undefined) {
        searchValue = "";
    }
    var restAPI = requestUrl + "?action=GetDownloadList&searchBox=" + searchValue;
    var noErrorWasThrown = true;
    var jqxhr = jQuery.getJSON(restAPI, {})
    .done(function(data) {
        if(showDialog == true) {
            jQuery(function () {
                jQuery("#dialogDownload").dialog("close");
            });
        }
        console.log(restAPI);
        var itemCount = data.items.length;
        if(itemCount == undefined) {
            // Handle Error that may have come back
            noErrorWasThrown = false;
            var ex = data.exception;
            console.log(ex);
            jQuery('#dialogDownload').html(ex);
        }
        if(noErrorWasThrown){
            console.log("download list retrieved. " + itemCount + " results returned");
            populateTable(data);
        }
    })
    .fail(function() {
        console.log("search failed");
    })
    .always(function() {
        console.log("search complete");
    });
}

function openDownloadStudyDialog(containerId, adminOffices)
{
    // Display a dialog asking the user to select admin office
    jQuery( function() {
        jQuery( "#dialogDownload" ).dialog({
            buttons: {
                Download: function() {
                    if(jQuery("#adminOfficeSelectControl").val() == null) {
                        alert("Must select an admin office");
                        return;
                    }
                    downloadStudy(containerId, jQuery("#adminOfficeSelectControl").val());
                }
            },
            title: "Download Study"
        });
        var adminOfficeSelect = "<select ID='adminOfficeSelectControl'>";
        var currentAdminOfficeId;
        var adminOfficesArray = adminOffices.split(",");
        for(var i = 0; i < adminOfficesArray.length; i++) {
            currentAdminOfficeId = adminOfficesArray[i];
            adminOfficeSelect += "<option value='" + currentAdminOfficeId + "'>" + currentAdminOfficeId + "</option>";
        }
        adminOfficeSelect += "</select>";
        jQuery("#dialogDownload").html("Ready to download study.<br><br>Choose an admin office: " + adminOfficeSelect);
    } );
}

function downloadStudy(containerId, adminOfficeId)
{
    // Display a dialog informing the user of progress
    jQuery( function() {
        jQuery( "#dialogDownload" ).dialog({
            buttons: {
            },
            title: "Downloading Study..."
        });
        jQuery("#dialogDownload").html("Downloading the study. This will take a few moments...");
    } );

    // Download the study and when done change the dialog content to reflect
    var restAPI = requestUrl + "?action=DownloadStudy&containerId=" + containerId + "&adminOfficeId=" + adminOfficeId;
    var jqxhr = jQuery.getJSON(restAPI, {})
    .done(function(data) {
        var downloadDocumentsDetails = {};
        downloadDocumentsDetails["study"] = data.study;
        var documentsToDownloadArray = data.documentsToDownloadArray;
        var studyLink = data.studyLink;
        jQuery(function(){
            if(typeof downloadDocumentsScope == "undefined") {
                downloadDocumentsScope = {};
            }
            downloadDocumentsScope.handleFailure = function(jqXHR, data, textStatus){
                var errorString = '<p class="Error">Error thrown attempting to download documents from IRB Exchange. Please contact a system administrator. ' + data + "</p>";
                jQuery('#dialogDownload').append(errorString);
                jQuery("body").css("cursor", "default");
            }

            downloadDocumentsScope.downloadDocument = function(index){
                downloadDocumentsDetails["documentPoRef"] = downloadDocumentsScope.documentsToDownload[index].document;
                downloadDocumentsDetails["entity"] = downloadDocumentsScope.documentsToDownload[index].entity;
                downloadDocumentsDetails["attributePath"] = downloadDocumentsScope.documentsToDownload[index].attributePath;
                downloadDocumentsDetails["exchangeRef"] = downloadDocumentsScope.documentsToDownload[index].exchangeRef;
                PortalTools.callRemoteMethod("ClickIRBRemoteMethods.downloadDocument", JSON.stringify(downloadDocumentsDetails)).done(function(data, textStatus, jqXHR){
                    if(data != "success"){
                        downloadDocumentsScope.handleFailure(jqXHR, data, textStatus);
                        return;
                    }
                    downloadDocumentsScope.downloadedCount++;
                    jQuery('#dialogDownload').html('downloaded ' + downloadDocumentsScope.downloadedCount + ' of ' + downloadDocumentsScope.documentsToDownload.length + ' ... ');
                    if(downloadDocumentsScope.downloadedCount == downloadDocumentsScope.documentsToDownload.length){
                        jQuery( function() {
                            jQuery( "#dialogDownload" ).dialog({
                                buttons: {
                                    Close: function() {
                                        jQuery( this ).dialog( "close" );
                                    }
                                },
                                title: "Study Downloaded"
                            });
                        } );
                        jQuery("body").css("cursor", "default");
                        jQuery("#dialogDownload").html("Study downloaded successfully!<br><br>You may click the link below to navigate to the new external study:<br><br>" + studyLink);
                        searchItemsToDownload(false);
                    }
                }).fail(downloadDocumentsScope.handleFailure);
            }

            downloadDocumentsScope.documentsToDownload = JSON.parse(documentsToDownloadArray, null);

            var documentsToDownloadCount = downloadDocumentsScope.documentsToDownload.length;
            if (documentsToDownloadCount > 0) {
                jQuery("#dialogDownload").html('<strong>' + documentsToDownloadCount + ' documents still to be downloaded</strong>');
                jQuery("body").css("cursor", "progress");
                downloadDocumentsScope.downloadedCount = 0;
                for (var i = 0; i < documentsToDownloadCount; i++) {
                    downloadDocumentsScope.downloadDocument(i);
                }
            } else {
                jQuery( function() {
                    jQuery( "#dialogDownload" ).dialog({
                        buttons: {
                            Close: function() {
                                jQuery( this ).dialog( "close" );
                            }
                        },
                        title: "Study Downloaded"
                    });
                } );
                jQuery("body").css("cursor", "default");
                jQuery("#dialogDownload").html("Study downloaded successfully!<br><br>You may click the link below to navigate to the new external study:<br><br>" + studyLink);
                searchItemsToDownload(false);
            }
        });
    })
    .fail(function() {
        console.log("download failed");
        jQuery( function() {
            jQuery( "#dialogDownload" ).dialog({
                buttons: {
                    Close: function() {
                        jQuery( this ).dialog( "close" );
                    }
                },
                title: "Download Study Failed"
            });
        } );
        jQuery("#dialogDownload").html("Failed to download study.<br><br>Contact your system administrator.");
    })
    .always(function() {
        console.log("download complete");
    });
}

function downloadRni(containerId, rniItemId, accountName)
{
    // Display a dialog informing the user of progress
    jQuery( function() {
        jQuery( "#dialogDownload" ).dialog({
            buttons: {
            },
            title: "Downloading RNI..."
        });
        jQuery("#dialogDownload").html("Downloading the RNI. This will take a few moments...");
    } );

    // Download the RNI and when done change the dialog content to reflect
    var restAPI = requestUrl + "?action=DownloadRni&containerId=" + containerId + "&rniItemId=" + rniItemId + "&accountName=" + accountName;
    var noErrorWasThrown = true;
    var jqxhr = jQuery.getJSON(restAPI, {})
    .done(function(data) {
        var downloadDocumentsDetails = {};
        downloadDocumentsDetails["study"] = data.study;
        var documentsToDownloadArray = data.documentsToDownloadArray;
        var rniLink = data.rniLink;
        jQuery(function() {
            if (typeof downloadDocumentsScope == "undefined") {
                downloadDocumentsScope = {};
            }
            downloadDocumentsScope.handleFailure = function (jqXHR, data, textStatus) {
                var errorString = '<p class="Error">Error thrown attempting to download documents from IRB Exchange. Please contact a system administrator. ' + data + "</p>";
                jQuery('#dialogDownload').append(errorString);
                jQuery("body").css("cursor", "default");
            }

            downloadDocumentsScope.downloadDocument = function (index) {
                downloadDocumentsDetails["documentPoRef"] = downloadDocumentsScope.documentsToDownload[index].document;
                downloadDocumentsDetails["entity"] = downloadDocumentsScope.documentsToDownload[index].entity;
                downloadDocumentsDetails["attributePath"] = downloadDocumentsScope.documentsToDownload[index].attributePath;
                downloadDocumentsDetails["exchangeRef"] = downloadDocumentsScope.documentsToDownload[index].exchangeRef;
                PortalTools.callRemoteMethod("ClickIRBRemoteMethods.downloadDocument", JSON.stringify(downloadDocumentsDetails)).done(function (data, textStatus, jqXHR) {
                    if (data != "success") {
                        downloadDocumentsScope.handleFailure(jqXHR, data, textStatus);
                        return;
                    }
                    downloadDocumentsScope.downloadedCount++;
                    jQuery('#dialogDownload').html('downloaded ' + downloadDocumentsScope.downloadedCount + ' of ' + downloadDocumentsScope.documentsToDownload.length + ' ... ');
                    if (downloadDocumentsScope.downloadedCount == downloadDocumentsScope.documentsToDownload.length) {
                        jQuery(function () {
                            jQuery("#dialogDownload").dialog({
                                buttons: {
                                    Close: function () {
                                        jQuery(this).dialog("close");
                                    }
                                },
                                title: "RNI Downloaded"
                            });
                        });
                        jQuery("body").css("cursor", "default");
                        jQuery("#dialogDownload").html("RNI downloaded successfully!<br><br>You may click the link below to navigate to the new external RNI:<br><br>" + rniLink);
                        searchItemsToDownload(false);
                    }
                }).fail(downloadDocumentsScope.handleFailure);
            }

            downloadDocumentsScope.documentsToDownload = JSON.parse(documentsToDownloadArray, null);

            var documentsToDownloadCount = downloadDocumentsScope.documentsToDownload.length;
            if (documentsToDownloadCount > 0) {
                jQuery("#dialogDownload").html('<strong>' + documentsToDownloadCount + ' documents still to be downloaded</strong>');
                jQuery("body").css("cursor", "progress");
                downloadDocumentsScope.downloadedCount = 0;
                for (var i = 0; i < documentsToDownloadCount; i++) {
                    downloadDocumentsScope.downloadDocument(i);
                }
            } else {
                jQuery(function () {
                    jQuery("#dialogDownload").dialog({
                        buttons: {
                            Close: function () {
                                jQuery(this).dialog("close");
                            }
                        },
                        title: "RNI Downloaded"
                    });
                });
                jQuery("body").css("cursor", "default");
                jQuery("#dialogDownload").html("RNI downloaded successfully!<br><br>You may click the link below to navigate to the new external RNI:<br><br>" + rniLink);
                searchItemsToDownload(false);
            }
        })
    })
    .fail(function() {
        console.log("download failed");
        jQuery( function() {
            jQuery( "#dialogDownload" ).dialog({
                buttons: {
                    Close: function() {
                        jQuery( this ).dialog( "close" );
                    }
                },
                title: "Download RNI Failed"
            });
        } );
        jQuery("#dialogDownload").html("Failed to download RNI.<br><br>Contact your system administrator.");
    })
    .always(function() {
        console.log("download complete");
    });
}

Technical notes about this script example:

  • When the page loads, it calls searchItemsToDownload function to get the download list from the IRB Exchange and dislay it.
  • The populateTable function forms HTML based on data from the IRB Exchange and displays it to the page. It puts the HTML into the downloadList group and it contains the HTML necessary to display the Download button that will download the submission when clicked.
  • The searchItemsToDownload function gets the download list from the IRB Exchange and calls the populateTable function to display it. This makes a request to the DownloadListRequest Site Designer page that gets the data from the IRB Exchange. While the request is being made, the dialogDownload group is turned into a jQuery dialog to display a progress message.
  • The openDownloadStudyDialog function runs when the user clicks the Download button for a study where more than one IRB office is available to assign to it. It turns the dialogDownload group into a jQuery dialog that has an IRB office chooser and Download button on it. When the user clicks the Download button, it validates that they selected an IRB office and initiates the download operation.
  • The downloadStudy function turns the dialogDownload group into a jQuery progress indicator while it does its work to download the chosen study. It makes a request to the DownloadListRequest Site Designer page to get the study's data. On the callback function, it takes JSON metadata returned and uses it to download any documents on the study. For each document, it makes a remote call to the ClickIRBRemoteMethods.downloadDocument method to download the document content in a separate transaction in order to avoid timeouts.
  • The downloadRni function works similarly to the downloadStudy function, but for downloading reportable new information submissions.

Server Side Script Example

function generateHtml(sch)
{
    var baseUrl = sch.fullUrlFromUnsUrl(""); //Right now this points to the local machine

    //construct the url to be used for restfull calls.
    var html = "<script type='text/javascript'>";
    html += "var requestUrl = \""+ baseUrl +"Applications/IRB/Exchange/DownloadListRequest\";\n";
    html += "</script>";
    return html;
}

Technical note about this script example:

  • This script is needed to put HTML on the page that runs a client side script that sets a variable with the value of the request URL to the DownloadListRequest Site Designer page.

Implementing the Request Handler Page

Create a Site Designer page at Applications:\IRB\Exchange\DownloadListRequest. Define the httpResponse script for the Page control.

httpResponse Script Example

function httpResponse(sch) {
    try {
        // Strings representing the different actions available
        var GET_DOWNLOAD_LIST_ACTION_STR = "GetDownloadList";
        var DOWNLOAD_STUDY_ACTION_STR = "DownloadStudy";
        var DOWNLOAD_RNI_ACTION_STR = "DownloadRni";

        // Get the action string so we know what action is desired
        sch.addHttpHeader("Content-Type","application/json");
        var html = {};
        var action = sch.QueryString("action");
        switch(action) {
            case GET_DOWNLOAD_LIST_ACTION_STR:
                var searchStr = sch.QueryString("searchBox");

                // Get itemsToDownload from exchange filtered to name in search textbox that haven't been downloaded yet
                var activeAdminOffices = ApplicationEntity.getResultSet("_AdminOffice").query("customAttributes.disabled = False and customAttributes.officeType.ID = 'ID00000001' and customAttributes.iRBExchangeAccountName is not null");
                var activeAdminOfficesElements = activeAdminOffices.elements();
                var activeAdminOfficesCount = activeAdminOfficesElements.count();
                var exchangeClient, itemsToDownload, items, currentAccountName, currentAdminOffice, adminOfficesJson, adminOfficesForAccountName, adminOfficesForAccountNameCount, isUniqueAccountName;
                html.items = [];
                var accountNames = [];
                for(var i = 1; i <= activeAdminOfficesCount; i++) {
                    currentAdminOffice = activeAdminOfficesElements.item(i);
                    currentAccountName = currentAdminOffice.getQualifiedAttribute("customAttributes.iRBExchangeAccountName");
                    if(currentAccountName != null) {
                        isUniqueAccountName = true;
                        for(var j = 0; j < accountNames.length; j++) {
                            if(accountNames[j] == currentAccountName) {
                                isUniqueAccountName = false;
                            }
                        }
                        if(isUniqueAccountName == true) {
                            accountNames.push(currentAccountName);
                            exchangeClient = ClickIRBUtils.getExchangeClient(currentAccountName);
                            itemsToDownload = exchangeClient.GetDownloadList();
                            items = JSON.parse(itemsToDownload, null);
                            adminOfficesJson = [];
                            adminOfficesForAccountName = activeAdminOffices.query("customAttributes.iRBExchangeAccountName = '" + currentAccountName + "'").elements();
                            adminOfficesForAccountNameCount = adminOfficesForAccountName.count();
                            for (var j = 1; j <= adminOfficesForAccountNameCount; j++) {
                                adminOfficesJson.push(adminOfficesForAccountName.item(j).ID);
                            }
                            for (var j = 0; j < items.length; j++) {
                                items[j].accountName = currentAccountName;
                                items[j].adminOffices = adminOfficesJson;
                                if (adminOfficesForAccountNameCount > 1) {
                                    items[j].clientFunctionName = "openDownloadStudyDialog";
                                } else {
                                    items[j].clientFunctionName = "downloadStudy";
                                }
                                html.items.push(items[j]);
                            }
                        }
                    }
                }
                break;

            case DOWNLOAD_STUDY_ACTION_STR:
                var containerId = sch.QueryString("containerId");
                var adminOfficeId = sch.QueryString("adminOfficeId");

                // Get rni item from Exchange for the rni selected to download
                var adminOfficeMatches = ApplicationEntity.getResultSet("_AdminOffice").query("ID = '" + adminOfficeId + "'").elements();
                if(adminOfficeMatches.count() != 1) {
                    throw new Error("Unexpected count of _AdminOffice matching ID " + adminOfficeId);
                }
                var adminOfficeAccountName = adminOfficeMatches.item(1).getQualifiedAttribute("customAttributes.iRBExchangeAccountName");
                var exchangeClient = ClickIRBUtils.getExchangeClient(adminOfficeAccountName);
                var studyJsonStr = exchangeClient.GetStudy(null, containerId);
                if(studyJsonStr == null) {
                    throw new Error("Study JSON returned from Exchange GetStudy method unexpectedly null");
                }

                var studyJson = JSON.parse(studyJsonStr, null);
                studyJson.adminOfficeId = adminOfficeId;

                // Create the external rni and local site, with data, including container ID of rni
                html = _IRBSubmission.createExternalStudyFromExchange(studyJson, exchangeClient);
                var study = wom.getEntityFromString(html.study);
                html.studyLink = "<a style='text-decoration: underline;' href='" + study.getUrl() + "'>" + study.name + "</a>";

                break;

            case DOWNLOAD_RNI_ACTION_STR:
                var containerId = sch.QueryString("containerId");
                var rniItemId = sch.QueryString("rniItemId");
                var accountName = sch.QueryString("accountName");

                // Get RNI item from Exchange for the RNI selected to download
                var exchangeClient = ClickIRBUtils.getExchangeClient(accountName);
                var rniJsonStr = exchangeClient.GetRni(null, containerId, null, rniItemId);
                if(rniJsonStr == null) {
                    throw new Error("RNI JSON returned from Exchange GetRni method unexpectedly null");
                }

                var rniJson = JSON.parse(rniJsonStr, null);

                // Create the RNI
                html = _IRBSubmission.createExternalRniFromExchange(rniJson, exchangeClient);
                var rni = wom.getEntityFromString(html.rni);

                // Put a link to the external rni in the HTML
                html.rniLink = "<a style='text-decoration: underline;' href='" + rni.getUrl() + "'>" + rni.name + "</a>";

                break;

            default:
                throw new Error("Site Designer page Applications: IRB/DownloadList httpResponse script: Unexpected action string received: " + action);
                break;
        }

        sch.appendHTML(JSON.stringify(html, null, null));
    } catch (e) {
        wom.log("EXCEPTION /Applications/IRB/Exchange/DownloadList " + e.description);
        var errorJson = {"exception": e.description};
        sch.appendHtml(JSON.stringify(errorJson, null, null));
    }
    return 0;
}

Technical notes about this script example:

  • The different actions required of this script are divided using a switch statement.
  • The GetDownloadList action gets the download list from the IRB Exchange. It collects the unique IRB Exchange accounts configured on active IRB offices to get its data. It calls the IrbExchange.GetDownloadList method to get the data from the IRB Exchange.
  • The DownloadStudy action downloads a chosen study from the IRB Exchange. It calls the IrbExchange.GetStudy method to get the data from the IRB Exchange and passes it off to a call to the _IRBSubmission.createExternalStudyFromExchange method to have the study created and set all the data on it.
  • The DownloadRni action downloads a reportable new information submission from the IRB Exchange. It calls the IrbExchange.GetRni method to get the data from the IRB Exchange and passes it off to a call to the _IRBSubmission.createExternalRniFromExchange method to have the reportable new information submission created and set all the data on it.
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.