/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.csniper.webapp.statistics;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.mutable.MutableInt;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import de.tudarmstadt.ukp.csniper.webapp.statistics.model.AggregatedEvaluationResult;
import de.tudarmstadt.ukp.csniper.webapp.statistics.page.StatisticsPage.TypeStatistics;
import de.tudarmstadt.ukp.csniper.webapp.statistics.page.StatisticsPage2.ItemStatistics;
import de.tudarmstadt.ukp.dkpro.statistics.agreement.AnnotationStudy;
import de.tudarmstadt.ukp.dkpro.statistics.agreement.IAnnotationStudy;
import de.tudarmstadt.ukp.dkpro.statistics.agreement.MultiRaterPiAgreement;
/**
* Original code from org.apache.wicket.examples.repeater.SortableContactDataProvider, modified and
* extended.
*
* @author Erik-Lân Do Dinh
*
*/
public class SortableAggregatedEvaluationResultDataProvider
extends SortableDataProvider<AggregatedEvaluationResult, String>
{
public enum ResultFilter
{
CORRECT("Correct", Color.GREEN),
WRONG("Wrong", Color.RED),
DISPUTED("Disputed", Color.ORANGE),
INCOMPLETE("Incomplete", Color.LIGHT_GRAY),
UNKNOWN("Unknown", Color.DARK_GRAY);
private String label;
private Color color;
private ResultFilter(String aLabel, Color aColor)
{
label = aLabel;
color = aColor;
}
public String getLabel()
{
return label;
}
public Color getColor()
{
return color;
}
}
private static final long serialVersionUID = -4680400414358831292L;
private List<AggregatedEvaluationResult> aerList;
private List<AggregatedEvaluationResult> limitedResults;
private Set<String> users;
private List<ResultFilter> filters;
private boolean filterChanged;
private String lastSortProperty;
private boolean lastSortOrder;
private SortableAggregatedEvaluationResultDataProvider()
{
// set default sort
setSort("id", SortOrder.ASCENDING);
}
public SortableAggregatedEvaluationResultDataProvider(
List<AggregatedEvaluationResult> aAerList, Collection<String> aUsers)
{
this();
aerList = aAerList;
users = new HashSet<String>(aUsers);
filters = new ArrayList<ResultFilter>();
}
/**
* @see org.apache.wicket.markup.repeater.data.IDataProvider#iterator(int, int)
*/
@Override
public Iterator<AggregatedEvaluationResult> iterator(long aFirst, long aCount)
{
// Apply paging
updateView();
return limitedResults.subList((int) aFirst, (int) Math.min(aFirst + aCount, limitedResults.size()))
.iterator();
}
/**
* @see org.apache.wicket.markup.repeater.data.IDataProvider#size()
*/
@Override
public long size()
{
updateView();
return limitedResults.size();
}
private void updateView()
{
final SortParam<String> sp = getSort();
// filter
if (!sp.getProperty().equals(lastSortProperty) || (lastSortOrder != sp.isAscending())
|| filterChanged) {
limitedResults = new ArrayList<AggregatedEvaluationResult>();
for (AggregatedEvaluationResult aer : aerList) {
if (getFilters().contains(aer.getClassification())) {
limitedResults.add(aer);
}
}
}
// sort
Collections.sort(limitedResults, new Comparator<AggregatedEvaluationResult>()
{
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public int compare(AggregatedEvaluationResult aO1, AggregatedEvaluationResult aO2)
{
try {
Comparable v1 = (Comparable) PropertyUtils.getNestedProperty(aO1,
sp.getProperty());
Comparable v2 = (Comparable) PropertyUtils.getNestedProperty(aO2,
sp.getProperty());
return sp.isAscending() ? v1.compareTo(v2) : v2.compareTo(v1);
}
catch (Exception e) {
throw new IllegalStateException(e);
}
}
});
lastSortProperty = sp.getProperty();
lastSortOrder = sp.isAscending();
filterChanged = false;
}
/**
* @see org.apache.wicket.markup.repeater.data.IDataProvider#model(java.lang.Object)
*/
@Override
public IModel<AggregatedEvaluationResult> model(AggregatedEvaluationResult aObject)
{
return new Model<AggregatedEvaluationResult>(aObject);
}
/**
* Counts how many results there are in each ResultFilter category.
*
* @return map
*/
@Deprecated
public Map<ResultFilter, Integer> getClassifications()
{
Map<ResultFilter, Integer> classifications = new HashMap<ResultFilter, Integer>();
for (ResultFilter filter : ResultFilter.values()) {
classifications.put(filter, 0);
}
for (AggregatedEvaluationResult aer : aerList) {
ResultFilter current = aer.getClassification();
classifications.put(current, classifications.get(current) + 1);
}
return classifications;
}
public double computeInterAnnotatorAgreement()
{
IAnnotationStudy study = new AnnotationStudy(users.size());
for (AggregatedEvaluationResult aer : aerList) {
// only use items which are annotated by all users
if (aer.getUserRatio() == 1) {
study.addItemAsArray(aer.getOrderedMarks());
}
}
MultiRaterPiAgreement m = new MultiRaterPiAgreement(study);
return m.calculateAgreement();
}
public ItemStatistics getItemStatistics()
{
ItemStatistics istats = new ItemStatistics();
istats.users = users.size();
istats.items = aerList.size();
for (AggregatedEvaluationResult aer : aerList) {
istats.correctVotes += aer.getCorrect();
istats.wrongVotes += aer.getWrong();
ResultFilter classification = aer.getClassification();
if (classification == ResultFilter.CORRECT) {
istats.truePositives++;
}
else if (classification == ResultFilter.WRONG) {
istats.falsePositive++;
}
}
istats.unknown = istats.items - istats.truePositives - istats.falsePositive;
istats.fleissKappa = computeInterAnnotatorAgreement();
return istats;
}
public Map<String, TypeStatistics> getTypeStatistics(Map<String, TypeStatistics> aStatsMap,
Map<String, MutableInt> aUserStats)
{
for (AggregatedEvaluationResult aer : aerList) {
String type = aer.getItem().getType();
TypeStatistics tstats = aStatsMap.get(type);
if (tstats == null) {
tstats = new TypeStatistics();
aStatsMap.put(type, tstats);
}
boolean countForUser = true;
switch (aer.getClassification()) {
case CORRECT:
tstats.correct++;
countForUser = true;
break;
case WRONG:
tstats.wrong++;
countForUser = true;
break;
case DISPUTED:
tstats.disputed++;
countForUser = true;
break;
case INCOMPLETE:
tstats.incomplete++;
break;
default:
// do nothing
}
if (countForUser) {
Set<String> users = aer.getUsers(true);
for (String user : users) {
MutableInt count = aUserStats.get(user);
if (count == null) {
count = new MutableInt();
aUserStats.put(user, count);
}
count.increment();
}
}
}
return aStatsMap;
}
public List<ResultFilter> getFilters()
{
return filters;
}
public void setFilters(List<ResultFilter> aFilters)
{
if (!(aFilters.containsAll(filters) && filters.containsAll(aFilters))) {
filters = aFilters;
filterChanged = true;
}
}
}