edu.cudenver.bios.glimmpse.client.panels.ResultsDisplayPanel.java Source code

Java tutorial

Introduction

Here is the source code for edu.cudenver.bios.glimmpse.client.panels.ResultsDisplayPanel.java

Source

/*
 * User Interface for the GLIMMPSE Software System.  Allows
 * users to perform power, sample size, and detectable difference
 * calculations. 
 * 
 * Copyright (C) 2010 Regents of the University of Colorado.  
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package edu.cudenver.bios.glimmpse.client.panels;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ErrorEvent;
import com.google.gwt.event.dom.client.ErrorHandler;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Hidden;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.AbstractDataTable.ColumnType;
import com.google.gwt.visualization.client.visualizations.Table;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.NamedNodeMap;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.XMLParser;

import edu.cudenver.bios.glimmpse.client.ChartRequestBuilder;
import edu.cudenver.bios.glimmpse.client.Glimmpse;
import edu.cudenver.bios.glimmpse.client.GlimmpseConstants;
import edu.cudenver.bios.glimmpse.client.StudyDesignManager;
import edu.cudenver.bios.glimmpse.client.listener.ChartOptionsListener;
import edu.cudenver.bios.glimmpse.client.listener.ConfidenceIntervalListener;
import edu.cudenver.bios.glimmpse.client.listener.SolvingForListener;

/**
 * Final results display panel
 * @author Sarah Kreidler
 *
 */
public class ResultsDisplayPanel extends WizardStepPanel
        implements ChartOptionsListener, SolvingForListener, ConfidenceIntervalListener {
    // columns associated with each quantity in the data table
    private static final int COLUMN_ID_TEST = 0;
    private static final int COLUMN_ID_ACTUAL_POWER = 1;
    private static final int COLUMN_ID_TOTAL_SAMPLE_SIZE = 2;
    private static final int COLUMN_ID_BETA_SCALE = 3;
    private static final int COLUMN_ID_SIGMA_SCALE = 4;
    private static final int COLUMN_ID_ALPHA = 5;
    private static final int COLUMN_ID_NOMINAL_POWER = 6;
    private static final int COLUMN_ID_POWER_METHOD = 7;
    private static final int COLUMN_ID_QUANTILE = 8;
    private static final int COLUMN_ID_CI_LOWER = 9;
    private static final int COLUMN_ID_CI_UPPER = 10;

    private static final String STYLE_RESULT_BUTTON = "resultsPanelButton";
    private static final String STYLE_SEPARATOR = "separator";
    private static final int STATUS_CODE_OK = 200;
    private static final int STATUS_CODE_CREATED = 201;
    private static final String POWER_URL = Glimmpse.constants.powerServiceURL() + "/power";
    private static final String SAMPLE_SIZE_URL = Glimmpse.constants.powerServiceURL() + "/samplesize";

    private static final String CHART_INPUT_NAME = "chart";
    private static final String SAVE_INPUT_NAME = "save";
    private static final String FILENAME_INPUT_NAME = "filename";
    private static final String SAVE_CSV_FILENAME = "powerResults.csv";
    private NumberFormat doubleFormatter = NumberFormat.getFormat("0.0000");

    // wait dialog
    protected DialogBox waitDialog;

    // google visualization api data table to hold results
    protected DataTable resultsData;

    // tabular display of results
    protected VerticalPanel resultsTablePanel = new VerticalPanel();
    protected Table resultsTable = new Table(resultsData, null);

    // curve display
    protected VerticalPanel resultsCurvePanel = new VerticalPanel();

    // error display
    protected VerticalPanel errorPanel = new VerticalPanel();
    protected HTML errorHTML = new HTML();
    // I tried to build the curves with Google Chart Api, but the scatter chart
    // didn't have enough control over line types, etc.  Thus, I rolled my own
    // restlet on top of JFreeChart.  Images are retrieved via GET
    protected Image powerCurveImage = new Image();
    protected Image legendImage = new Image();
    // matrix popup panel - allows users to view the actual matrices produced for the calculations
    // and hey, it sure is nice for debugging
    protected PopupPanel matrixPopup = new PopupPanel();
    protected MatrixDisplayPanel matrixDisplayPanel = new MatrixDisplayPanel();
    protected Button showMatrixPopupButton = new Button("View Matrices used for these results", new ClickHandler() {
        public void onClick(ClickEvent event) {
            matrixPopup.center();
        }
    });

    // blank target window - for saving images
    protected FormPanel saveForm = new FormPanel("_blank");
    protected Hidden saveEntityBodyHidden = new Hidden(CHART_INPUT_NAME);
    protected Hidden saveFilenameHidden = new Hidden(FILENAME_INPUT_NAME);
    protected Hidden saveHidden = new Hidden(SAVE_INPUT_NAME);
    // options for display of data
    protected boolean showCurve = false;
    protected ChartRequestBuilder chartRequestBuilder = null;
    // indicates whether we are solving for power, sample size, or effect size
    protected SolutionType solutionType = GlimmpseConstants.DEFAULT_SOLUTION;
    // pointer to the overall wizard panel which can generate the entity body from the other panels
    protected StudyDesignManager manager;

    protected boolean showCI = false;

    public ResultsDisplayPanel(StudyDesignManager manager) {
        super();
        this.manager = manager;
        complete = true;
        VerticalPanel panel = new VerticalPanel();

        // build the wait dialog
        buildWaitDialog();
        // build the data table 
        buildDataTable();
        // build the display panels
        buildErrorPanel();
        buildCurvePanel();
        buildTablePanel();
        buildMatrixPopup();

        // layout the panel
        panel.add(errorPanel);
        panel.add(resultsCurvePanel);
        panel.add(resultsTablePanel);

        // set style
        panel.setStyleName(GlimmpseConstants.STYLE_WIZARD_STEP_PANEL);

        // initialize
        initWidget(panel);
    }

    private void buildErrorPanel() {
        errorPanel.add(errorHTML);
        errorPanel.setVisible(false);
    }

    private void buildMatrixPopup() {
        VerticalPanel panel = new VerticalPanel();
        panel.add(matrixDisplayPanel);
        panel.add(new Button("Close", new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                matrixPopup.hide();
            }
        }));
        matrixPopup.add(panel);
    }

    private void buildCurvePanel() {
        HTML header = new HTML("Power Curve");
        HTML description = new HTML("");

        // add load callbacks
        powerCurveImage.addErrorHandler(new ErrorHandler() {

            @Override
            public void onError(ErrorEvent event) {
                hideWorkingDialog();
            }
        });
        powerCurveImage.addLoadHandler(new LoadHandler() {
            @Override
            public void onLoad(LoadEvent event) {
                hideWorkingDialog();
            }
        });
        // legend images
        legendImage.addLoadHandler(new LoadHandler() {
            @Override
            public void onLoad(LoadEvent event) {
                hideWorkingDialog();
            }
        });
        legendImage.addErrorHandler(new ErrorHandler() {

            @Override
            public void onError(ErrorEvent event) {
                hideWorkingDialog();
            }
        });
        // layout the image / legend
        Grid grid = new Grid(1, 2);
        grid.setWidget(0, 0, powerCurveImage);
        grid.setWidget(0, 1, legendImage);

        // layout the sub panel
        resultsCurvePanel.add(header);
        resultsCurvePanel.add(description);
        resultsCurvePanel.add(grid);

        // set style
        //      powerCurveImage.setStyleName(STYLE_POWER_CURVE_FRAME);
        //      legendImage.setStyleName(STYLE_POWER_CURVE_FRAME);
        resultsCurvePanel.setStyleName(GlimmpseConstants.STYLE_WIZARD_STEP_PANEL);
        resultsCurvePanel.addStyleDependentName(GlimmpseConstants.STYLE_WIZARD_STEP_SUBPANEL);
        header.setStyleName(GlimmpseConstants.STYLE_WIZARD_STEP_HEADER);
        header.addStyleDependentName(GlimmpseConstants.STYLE_WIZARD_STEP_SUBPANEL);
        description.setStyleName(GlimmpseConstants.STYLE_WIZARD_STEP_DESCRIPTION);
        description.addStyleDependentName(GlimmpseConstants.STYLE_WIZARD_STEP_SUBPANEL);
    }

    private void buildTablePanel() {
        HTML header = new HTML("Power Results");
        HTML description = new HTML("");

        // tools for saving the results as csv and viewing as a matrix
        HorizontalPanel panel = new HorizontalPanel();
        Button saveButton = new Button(Glimmpse.constants.toolsSaveCSV(), new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                saveTableData();
            }
        });
        Button viewMatrixButton = new Button(Glimmpse.constants.toolsViewMatrices(), new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                matrixPopup.center();
            }
        });
        panel.add(saveButton);
        panel.add(viewMatrixButton);
        // layout the sub panel
        resultsTablePanel.add(header);
        resultsTablePanel.add(description);
        resultsTablePanel.add(resultsTable);
        resultsTablePanel.add(panel);
        // set style
        saveButton.setStyleName(STYLE_RESULT_BUTTON);
        saveButton.addStyleDependentName(STYLE_SEPARATOR);
        viewMatrixButton.setStyleName(STYLE_RESULT_BUTTON);
        resultsTablePanel.setStyleName(GlimmpseConstants.STYLE_WIZARD_STEP_PANEL);
        resultsTablePanel.addStyleDependentName(GlimmpseConstants.STYLE_WIZARD_STEP_SUBPANEL);
        header.setStyleName(GlimmpseConstants.STYLE_WIZARD_STEP_HEADER);
        header.addStyleDependentName(GlimmpseConstants.STYLE_WIZARD_STEP_SUBPANEL);
        description.setStyleName(GlimmpseConstants.STYLE_WIZARD_STEP_DESCRIPTION);
        description.addStyleDependentName(GlimmpseConstants.STYLE_WIZARD_STEP_SUBPANEL);
    }

    @Override
    public void reset() {
        matrixDisplayPanel.reset();
        resultsData.removeRows(0, resultsData.getNumberOfRows());
        resultsTablePanel.setVisible(false);
        resultsCurvePanel.setVisible(false);
        errorPanel.setVisible(false);
    }

    @Override
    public void onEnter() {
        reset();
        sendPowerRequest();
    }

    private void buildDataTable() {
        // set up the columns in the data table
        resultsData = DataTable.create();
        resultsData.addColumn(ColumnType.STRING, "Test", GlimmpseConstants.COLUMN_NAME_TEST);
        resultsData.addColumn(ColumnType.NUMBER, "Actual Power", GlimmpseConstants.COLUMN_NAME_ACTUAL_POWER);
        resultsData.addColumn(ColumnType.NUMBER, "Total Sample Size", GlimmpseConstants.COLUMN_NAME_SAMPLE_SIZE);
        resultsData.addColumn(ColumnType.NUMBER, "Beta Scale", GlimmpseConstants.COLUMN_NAME_BETA_SCALE);
        resultsData.addColumn(ColumnType.NUMBER, "Sigma Scale", GlimmpseConstants.COLUMN_NAME_SIGMA_SCALE);
        resultsData.addColumn(ColumnType.NUMBER, "Alpha", GlimmpseConstants.COLUMN_NAME_ALPHA);
        resultsData.addColumn(ColumnType.NUMBER, "Nominal Power", GlimmpseConstants.COLUMN_NAME_NOMINAL_POWER);
        resultsData.addColumn(ColumnType.STRING, "Power Method", GlimmpseConstants.COLUMN_NAME_POWER_METHOD);
        resultsData.addColumn(ColumnType.NUMBER, "Quantile", GlimmpseConstants.COLUMN_NAME_QUANTILE);
        resultsData.addColumn(ColumnType.NUMBER, "Power Lower", GlimmpseConstants.COLUMN_NAME_CI_LOWER);
        resultsData.addColumn(ColumnType.NUMBER, "Power Upper", GlimmpseConstants.COLUMN_NAME_CI_UPPER);
    }

    private void buildWaitDialog() {
        waitDialog = new DialogBox();
        waitDialog.setGlassEnabled(true);
        HTML text = new HTML("Processing, Please Wait...");
        text.setStyleName("waitDialogText");
        waitDialog.setStyleName("waitDialog");
        waitDialog.setWidget(text);
    }

    private void showWorkingDialog() {
        waitDialog.center();
    }

    private void hideWorkingDialog() {
        waitDialog.hide();
    }

    private void showError(String message) {
        errorHTML.setHTML(message);
        errorPanel.setVisible(true);
        hideWorkingDialog();
    }

    private void showResults(String resultXML) {
        try {
            // parse the returned XML
            Document doc = XMLParser.parse(resultXML);
            NodeList powerListTags = doc.getElementsByTagName("powerList");
            if (powerListTags == null || powerListTags.getLength() != 1)
                throw new IllegalArgumentException("No results returned");
            NamedNodeMap attrList = powerListTags.item(0).getAttributes();
            if (attrList == null)
                throw new IllegalArgumentException("Invalid response from power server");

            Node countAttr = attrList.getNamedItem("count");
            if (countAttr == null)
                throw new IllegalArgumentException("Invalid response from power server");

            int count = Integer.parseInt(countAttr.getNodeValue());

            // fill the google visualization data table
            NodeList glmmPowerList = doc.getElementsByTagName("glmmPower");
            for (int powerIdx = 0; powerIdx < count; powerIdx++) {
                Node glmmPower = glmmPowerList.item(powerIdx);
                NamedNodeMap attrs = glmmPower.getAttributes();

                // add a blank row to the data table
                int row = resultsData.addRow();

                Node testNode = attrs.getNamedItem("test");
                if (testNode != null) {
                    resultsData.setCell(row, COLUMN_ID_TEST, testNode.getNodeValue(),
                            formatTestName(testNode.getNodeValue()), null);
                }

                Node actualPowerNode = attrs.getNamedItem("actualPower");
                if (actualPowerNode != null) {
                    resultsData.setCell(row, COLUMN_ID_ACTUAL_POWER,
                            Double.parseDouble(actualPowerNode.getNodeValue()),
                            formatDouble(actualPowerNode.getNodeValue()), null);
                }

                Node sampleSizeNode = attrs.getNamedItem("sampleSize");
                if (sampleSizeNode != null) {
                    resultsData.setCell(row, COLUMN_ID_TOTAL_SAMPLE_SIZE,
                            Integer.parseInt(sampleSizeNode.getNodeValue()), sampleSizeNode.getNodeValue(), null);
                }

                Node betaScaleNode = attrs.getNamedItem("betaScale");
                if (betaScaleNode != null) {
                    resultsData.setCell(row, COLUMN_ID_BETA_SCALE, Double.parseDouble(betaScaleNode.getNodeValue()),
                            betaScaleNode.getNodeValue(), null);
                }

                Node sigmaScaleNode = attrs.getNamedItem("sigmaScale");
                if (sigmaScaleNode != null) {
                    resultsData.setCell(row, COLUMN_ID_SIGMA_SCALE,
                            Double.parseDouble(sigmaScaleNode.getNodeValue()), sigmaScaleNode.getNodeValue(), null);
                }

                Node alphaNode = attrs.getNamedItem("alpha");
                if (alphaNode != null) {
                    resultsData.setCell(row, COLUMN_ID_ALPHA, Double.parseDouble(alphaNode.getNodeValue()),
                            alphaNode.getNodeValue(), null);
                }

                Node nominalPowerNode = attrs.getNamedItem("nominalPower");
                if (nominalPowerNode != null) {
                    resultsData.setCell(row, COLUMN_ID_NOMINAL_POWER,
                            Double.parseDouble(nominalPowerNode.getNodeValue()),
                            formatDouble(nominalPowerNode.getNodeValue()), null);
                }

                Node powerMethodNode = attrs.getNamedItem("powerMethod");
                if (powerMethodNode != null) {
                    resultsData.setCell(row, COLUMN_ID_POWER_METHOD, powerMethodNode.getNodeValue(),
                            formatPowerMethodName(powerMethodNode.getNodeValue()), null);
                }

                Node quantileNode = attrs.getNamedItem("quantile");
                if (quantileNode != null) {
                    resultsData.setCell(row, COLUMN_ID_QUANTILE, Double.parseDouble(quantileNode.getNodeValue()),
                            quantileNode.getNodeValue(), null);
                }

                Node ciLowerNode = attrs.getNamedItem("ciLower");
                if (ciLowerNode != null) {
                    resultsData.setCell(row, COLUMN_ID_CI_LOWER, Double.parseDouble(ciLowerNode.getNodeValue()),
                            formatDouble(ciLowerNode.getNodeValue()), null);
                }

                Node ciUpperNode = attrs.getNamedItem("ciUpper");
                if (ciUpperNode != null) {
                    resultsData.setCell(row, COLUMN_ID_CI_UPPER, Double.parseDouble(ciUpperNode.getNodeValue()),
                            formatDouble(ciUpperNode.getNodeValue()), null);
                }
            }

            if (chartRequestBuilder != null) {
                showCurveResults();
            } else {
                hideWorkingDialog();
            }
            resultsTable.draw(resultsData);
            resultsTablePanel.setVisible(true);
        } catch (Exception e) {
            showError(e.getMessage());
        }
    }

    private void showCurveResults() {
        // submit the result to the chart service
        resultsCurvePanel.setVisible(true);
        chartRequestBuilder.loadData(resultsData);
        powerCurveImage.setUrl(chartRequestBuilder.buildChartRequestURL());
        legendImage.setUrl(chartRequestBuilder.buildLegendRequestURL());
    }

    private String formatPowerMethodName(String name) {
        return name; // TODO
    }

    private String formatTestName(String name) {
        return name; // TODO
    }

    private String formatDouble(String valueStr) {
        try {
            double value = Double.parseDouble(valueStr);
            return doubleFormatter.format(value);
        } catch (Exception e) {
            return valueStr;
        }
    }

    private void sendPowerRequest() {
        showWorkingDialog();
        String requestEntityBody = manager.getPowerRequestXML();
        matrixDisplayPanel.loadFromXML(requestEntityBody);
        RequestBuilder builder = null;
        switch (solutionType) {
        case POWER:
            builder = new RequestBuilder(RequestBuilder.POST, POWER_URL);
            break;
        case TOTAL_N:
            builder = new RequestBuilder(RequestBuilder.POST, SAMPLE_SIZE_URL);
            break;
        }

        try {
            builder.setHeader("Content-Type", "text/xml");
            builder.sendRequest(requestEntityBody, new RequestCallback() {

                public void onError(Request request, Throwable exception) {
                    showError("Calculation failed: " + exception.getMessage());
                }

                public void onResponseReceived(Request request, Response response) {
                    if (STATUS_CODE_OK == response.getStatusCode()
                            || STATUS_CODE_CREATED == response.getStatusCode()) {
                        showResults(response.getText());
                    } else {
                        showError("Calculation failed: [HTTP STATUS " + response.getStatusCode() + "] "
                                + response.getText());
                    }
                }
            });
        } catch (Exception e) {
            showError("Failed to send the request: " + e.getMessage());
        }
    }

    @Override
    public void onShowCurve(ChartRequestBuilder chartRequestBuilder) {
        this.chartRequestBuilder = chartRequestBuilder;
    }

    @Override
    public void onSolvingFor(SolutionType solutionType) {
        this.solutionType = solutionType;
    }

    /**
     * Output the data table in CSV format
     * @return CSV formatted data
     */
    public String dataTableToCSV() {
        StringBuffer buffer = new StringBuffer();

        if (resultsData.getNumberOfRows() > 0) {
            // add the column headers
            for (int col = 0; col < resultsData.getNumberOfColumns(); col++) {
                if (col > 0)
                    buffer.append(",");
                buffer.append(resultsData.getColumnId(col));
            }
            buffer.append("\n");
            // now add the data
            for (int row = 0; row < resultsData.getNumberOfRows(); row++) {
                for (int col = 0; col < resultsData.getNumberOfColumns(); col++) {
                    if (col > 0)
                        buffer.append(",");
                    if (resultsData.getColumnType(col) == ColumnType.STRING)
                        buffer.append(resultsData.getValueString(row, col));
                    else
                        buffer.append(resultsData.getValueDouble(row, col));
                }
                buffer.append("\n");
            }
        }
        return buffer.toString();
    }

    public void saveTableData() {
        // submit the result to the file service
        manager.sendSaveRequest(dataTableToCSV(), SAVE_CSV_FILENAME);
    }

    @Override
    public void loadFromNode(Node node) {
        // nothing to do here. regenerated each time.
    }

    @Override
    public void onHasConfidenceInterval(boolean hasConfidenceInterval) {
        // TODO Auto-generated method stub

    }
}