de.tudarmstadt.ukp.clarin.webanno.monitoring.page.AgreementTable.java Source code

Java tutorial

Introduction

Here is the source code for de.tudarmstadt.ukp.clarin.webanno.monitoring.page.AgreementTable.java

Source

/*******************************************************************************
 * Copyright 2015
 * Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology
 * Technische Universitt Darmstadt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package de.tudarmstadt.ukp.clarin.webanno.monitoring.page;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.RefreshingView;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.resource.AbstractResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;

import de.tudarmstadt.ukp.clarin.webanno.brat.curation.AgreementUtils;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.AgreementUtils.AgreementResult;
import de.tudarmstadt.ukp.clarin.webanno.brat.curation.PairwiseAnnotationResult;
import de.tudarmstadt.ukp.clarin.webanno.monitoring.page.MonitoringPage.AgreementFormModel;
import de.tudarmstadt.ukp.clarin.webanno.support.AJAXDownload;
import de.tudarmstadt.ukp.clarin.webanno.support.DefaultRefreshingView;
import de.tudarmstadt.ukp.clarin.webanno.support.DescriptionTooltipBehavior;

public class AgreementTable extends Panel {
    private final static Log LOG = LogFactory.getLog(AgreementTable.class);

    private static final long serialVersionUID = 571396822546125376L;

    private RefreshingView<String> rows;

    private IModel<AgreementFormModel> settings;

    public AgreementTable(String aId) {
        super(aId);
    }

    public AgreementTable(String aId, IModel<AgreementFormModel> aSettings,
            IModel<PairwiseAnnotationResult> aModel) {
        super(aId, aModel);

        settings = aSettings;

        setOutputMarkupId(true);

        // This model makes sure we add a "null" dummy rater which accounts for the header columns
        // of the table.
        final IModel<List<String>> ratersAdapter = new AbstractReadOnlyModel<List<String>>() {
            private static final long serialVersionUID = 1L;

            @Override
            public List<String> getObject() {
                List<String> raters = new ArrayList<>();
                if (getModelObject() != null) {
                    raters.add(null);
                    raters.addAll(getModelObject().getRaters());
                }
                return raters;
            }
        };

        rows = new DefaultRefreshingView<String>("rows", ratersAdapter) {
            private static final long serialVersionUID = 1L;

            @Override
            protected void populateItem(final Item<String> aRowItem) {
                // Render regular row
                aRowItem.add(new DefaultRefreshingView<String>("cells", ratersAdapter) {
                    private static final long serialVersionUID = 1L;

                    @Override
                    protected void populateItem(Item<String> aCellItem) {
                        aCellItem.setRenderBodyOnly(true);

                        Fragment cell;
                        // Content cell
                        if (aRowItem.getIndex() == 0 || aCellItem.getIndex() == 0) {
                            cell = new Fragment("cell", "th", AgreementTable.this);
                        }
                        // Header cell
                        else {
                            cell = new Fragment("cell", "td", AgreementTable.this);
                        }

                        // Top-left cell
                        if (aRowItem.getIndex() == 0 && aCellItem.getIndex() == 0) {
                            cell.add(new Label("label", Model.of("")));
                        }
                        // Raters header horizontally
                        else if (aRowItem.getIndex() == 0 && aCellItem.getIndex() != 0) {
                            cell.add(new Label("label", Model.of(aCellItem.getModelObject())));
                        }
                        // Raters header vertically
                        else if (aRowItem.getIndex() != 0 && aCellItem.getIndex() == 0) {
                            cell.add(new Label("label", Model.of(aRowItem.getModelObject())));
                        }
                        // Upper diagonal
                        else if (aCellItem.getIndex() > aRowItem.getIndex()) {
                            AgreementResult result = AgreementTable.this.getModelObject()
                                    .getStudy(aRowItem.getModelObject(), aCellItem.getModelObject());

                            boolean noDataRater0 = result.isAllNull(result.getCasGroupIds().get(0));
                            boolean noDataRater1 = result.isAllNull(result.getCasGroupIds().get(1));

                            String label;
                            if (result.getStudy().getItemCount() == 0) {
                                label = "no positions";
                            } else if (noDataRater0 && noDataRater1) {
                                label = "no labels";
                            } else if (noDataRater0) {
                                label = "no labels from " + result.getCasGroupIds().get(0);
                            } else if (noDataRater1) {
                                label = "no labels from " + result.getCasGroupIds().get(1);
                            } else {
                                label = String.format("%.2f", result.getAgreement());
                            }

                            StringBuilder tooltipTitle = new StringBuilder();
                            tooltipTitle.append(result.getCasGroupIds().get(0));
                            tooltipTitle.append('/');
                            tooltipTitle.append(result.getCasGroupIds().get(1));

                            StringBuilder tooltipContent = new StringBuilder();
                            tooltipContent.append("Positions annotated:\n");
                            tooltipContent.append(String.format("- %s: %d/%d%n", result.getCasGroupIds().get(0),
                                    result.getNonNullCount(result.getCasGroupIds().get(0)),
                                    result.getStudy().getItemCount()));
                            tooltipContent.append(String.format("- %s: %d/%d%n", result.getCasGroupIds().get(1),
                                    result.getNonNullCount(result.getCasGroupIds().get(1)),
                                    result.getStudy().getItemCount()));
                            tooltipContent.append(String.format("Distinct labels used: %d%n",
                                    result.getStudy().getCategoryCount()));

                            Label l = new Label("label", Model.of(label));
                            cell.add(l);
                            l.add(makeDownloadBehavior(aRowItem.getModelObject(), aCellItem.getModelObject()));
                            DescriptionTooltipBehavior tooltip = new DescriptionTooltipBehavior(
                                    tooltipTitle.toString(), tooltipContent.toString());
                            tooltip.setOption("position", (Object) null);
                            l.add(tooltip);
                            l.add(new AttributeAppender("style", "cursor: pointer", ";"));
                        }
                        // Lower diagonal
                        else if (aCellItem.getIndex() < aRowItem.getIndex()) {
                            AgreementResult result = AgreementTable.this.getModelObject()
                                    .getStudy(aRowItem.getModelObject(), aCellItem.getModelObject());

                            String label = String.format("%d/%d", result.getCompleteSetCount(),
                                    result.getRelevantSetCount());

                            String tooltipTitle = "Details about annotations excluded from "
                                    + "agreement calculation";

                            StringBuilder tooltipContent = new StringBuilder();
                            if (result.isExcludeIncomplete()) {
                                tooltipContent.append(String.format("- Incomplete (missing): %d%n",
                                        result.getIncompleteSetsByPosition().size()));
                                tooltipContent.append(String.format("- Incomplete (not labeled): %d%n",
                                        result.getIncompleteSetsByLabel().size()));
                            }
                            tooltipContent
                                    .append(String.format("- Plurality: %d", result.getPluralitySets().size()));

                            Label l = new Label("label", Model.of(label));
                            DescriptionTooltipBehavior tooltip = new DescriptionTooltipBehavior(
                                    tooltipTitle.toString(), tooltipContent.toString());
                            tooltip.setOption("position", (Object) null);
                            l.add(tooltip);
                            l.add(new AttributeAppender("style", "cursor: help", ";"));
                            cell.add(l);
                        }
                        // Rest
                        else {
                            cell.add(new Label("label", Model.of("-")));
                        }

                        aCellItem.add(cell);
                    }
                });
                // Odd/even coloring is reversed here to account for the header row at index 0
                aRowItem.add(new AttributeAppender("class", (aRowItem.getIndex() % 2 == 0) ? "odd" : "even"));
            }
        };
        add(rows);
    }

    private Behavior makeDownloadBehavior(final String aKey1, final String aKey2) {
        return new AjaxEventBehavior("onclick") {
            private static final long serialVersionUID = 1L;

            @Override
            protected void onEvent(AjaxRequestTarget aTarget) {
                AJAXDownload download = new AJAXDownload() {
                    private static final long serialVersionUID = 1L;

                    @Override
                    protected IResourceStream getResourceStream() {
                        return new AbstractResourceStream() {
                            private static final long serialVersionUID = 1L;

                            @Override
                            public InputStream getInputStream() throws ResourceStreamNotFoundException {
                                try {
                                    AgreementResult result = AgreementTable.this.getModelObject().getStudy(aKey1,
                                            aKey2);

                                    switch (settings.getObject().exportFormat) {
                                    case CSV:
                                        return generateCsvReport(result);
                                    case DEBUG:
                                        return generateDebugReport(result);
                                    default:
                                        throw new IllegalStateException("Unknown export format ["
                                                + settings.getObject().exportFormat + "]");
                                    }
                                } catch (Exception e) {
                                    // FIXME Is there some better error handling here?
                                    LOG.error("Unable to generate agreement report", e);
                                    throw new ResourceStreamNotFoundException(e);
                                }
                            }

                            @Override
                            public void close() throws IOException {
                                // Nothing to do
                            }
                        };
                    }
                };
                getComponent().add(download);
                download.initiate(aTarget, "agreement" + settings.getObject().exportFormat.getExtension());
            }
        };
    }

    private InputStream generateCsvReport(AgreementResult aResult)
            throws UnsupportedEncodingException, IOException {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        try (CSVPrinter printer = new CSVPrinter(new OutputStreamWriter(buf, "UTF-8"), CSVFormat.RFC4180)) {
            AgreementUtils.toCSV(printer, aResult);
        }

        return new ByteArrayInputStream(buf.toByteArray());
    }

    private InputStream generateDebugReport(AgreementResult aResult) {
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        AgreementUtils.dumpAgreementStudy(new PrintStream(buf), aResult);
        return new ByteArrayInputStream(buf.toByteArray());
    }

    @Override
    protected void onComponentTag(ComponentTag tag) {
        checkComponentTag(tag, "table");
        super.onComponentTag(tag);
    }

    public PairwiseAnnotationResult getModelObject() {
        return (PairwiseAnnotationResult) getDefaultModelObject();
    }

    public void setModelObject(PairwiseAnnotationResult aAgreements2) {
        setDefaultModelObject(aAgreements2);
    }
}